NSMutableArray not retaining values outside of method - objective-c

Ive been trying to solve this for the past one hour, but still had no luck
I have an NSMutableArray instance variable which holds objects in the following class:The array successfully gets populated in the method i populate it in, but it shows up as empty in all other methods/classes
.h file...
#import "RedditPostItem.h"
#interface RedditRepository : NSObject
#property (nonatomic, strong) NSMutableArray *redditPosts; //<<** this
is the problem array
#property (nonatomic, strong,readwrite) NSDictionary *allJSONData;
#property (nonatomic, strong,readwrite) NSMutableData *incomingData;
- (void)getPosts;
- (void)printAllTitles;
#end
.m file
#implementation RedditRepository . . #synthesize
redditPosts=_redditPosts;
. .
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
self.redditPosts = [[NSMutableArray alloc] initWithCapacity:20];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
//parse json data
_allJSONData = [NSJSONSerialization JSONObjectWithData:_incomingData options:0 error:nil];
NSDictionary *dataDictionary = [_allJSONData objectForKey:#"data"];
NSArray *arrayOfChildren = [dataDictionary objectForKey:#"children"];
for (NSDictionary *diction in arrayOfChildren) {
NSDictionary *childrenData = [diction objectForKey:#"data"];
RedditPostItem *postItem = [[RedditPostItem alloc]initWithAPIResponse:childrenData];
//******************************* add to array.....
[self.redditPosts addObject:postItem];
}
//******* if i iterate through 'self.redditPosts' i can see everything uptill this point and the array successfully gets
populated//
}
//********* if I execute any operation from any other method in this
class or any other class...the array shows up as empty!!***//
- (void)printAllTitles{
if(self.redditPosts == nil) {
NSLog(#"array is empty....."); ///always shows up as empty for some reason<<<<<< }
}

Your array is being populated asynchronously - you're downloading from a URL in the background while your user interface continues to run in the foreground. The reason you're seeing it as empty is because it hasn't been populated yet. Your app should be written in such a way that it can deal with data not being available instantaneously and being able to update itself when the data becomes available.
If that doesn't get you on the right track, please ask a more specific question about the problem you're trying to solve.

Related

How to create a key per initiated object using NSUserdefaults

I have a custom NSObject and I instantiate this object using a manager.
My NSObjets are passed into a mutable array so that I can access their properties(name) via uipickerview once save method is initialised.
When I close my app, all my objects disappear as expected.
I would like to save my objects in the NSUserDefaults so I have implemented the NSUserdefaults in my manager. This is to save my objects so that they are loaded when the application is launched in the uipickerviewcontroller.
However my application crashes when I load the uipickerviewcontroller.
Here is the log after the crash:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[PacsServer encodeWithCoder:]: unrecognized selector sent to instance 0x7fd0a2e7fa20'
PacsServer.h
#import <Foundation/Foundation.h>
#interface PacsServer : NSObject
#property ( nonatomic) NSString *pacsName;
#property (nonatomic) NSString*username;
#property ( nonatomic) NSString*password;
#property (nonatomic) NSString *wadoUrl;
#property ( nonatomic) NSString *apiDirectory;
#property ( nonatomic) NSString *dataArrayIncludesUsernamePasswordWadoLoginWebServiceAndSecurity;
#end
pacsserver.m
#import "PacsServer.h"
#implementation PacsServer
#end
manager.h
#import <Foundation/Foundation.h>
#interface PacsServerManager : NSObject
+(instancetype)pacsManager ;
#property NSMutableArray *pacsServers;
#end
Manager.m
#import "PacsServerManager.h"
#import "PacsServer.h"
#implementation PacsServerManager
+(instancetype)pacsManager {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
-(id) init {
if(self =[super init]){
PacsServer *server= [[PacsServer alloc] init];
_pacsServers = [NSMutableArray arrayWithObjects:server, nil];
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSData *serverData = [NSKeyedArchiver archivedDataWithRootObject:_pacsServers];
[standardUserDefaults setObject:serverData forKey:#"server"];
[standardUserDefaults setObject:_normalArray forKey:#"Servers" ];
[standardUserDefaults synchronize];
}
return self;
}
#end
Here in the save method below I get the username, password and other fields from the text fields. To be able to save all these into keychain.
-I first save these in an array then pass this array to NSData using datawithJsonObject.
Then save this array as NSString into keychain
Add nsobjects via manager
#import "AddPACSViewController.h"
#import "PacsServerManager.h"
#import "PacsServer.h"
#import <KeychainItemWrapper.h>
#interface AddPACSViewController ()
#end- (void)saveForAddPacs:(id)sender {
.
.
.
.
NSString *pacsDataArray = [keychain objectForKey:(__bridge id)kSecValueData];
PacsServer *newPacsServer = [[PacsServer alloc] init];
newPacsServer.pacsName= pacsNameFromAddPacsView;
newPacsServer.dataArrayIncludesUsernamePasswordWadoLoginWebServiceAndSecurity = pacsDataArray;
[[PacsServerManager pacsManager].pacsServers addObject:newPacsServer];
[self performSegueWithIdentifier:#"Save" sender:self];
}
Access the objects in uipickerview
pickerviewcontroller.m
- (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
NSLog(#"Pacs data in settingsview:%#", [PacsServerManager pacsManager].pacsServers);
PacsServer *server = [[PacsServerManager pacsManager].pacsServers objectAtIndex:row];
NSString *name = server.pacsName;
NSLog(#"Servername:%#, and the data %#", server.pacsName, server.dataArrayIncludesUsernamePasswordWadoLoginWebServiceAndSecurity);
NSLog(#"%#", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);
return name;
}
Any help would be appreciated.
Thank you.
You need to address a couple of things here.
1) The exception is caused because of this line:
NSData *serverData = [NSKeyedArchiver archivedDataWithRootObject:_pacsServers];
When you archive any object, that object needs to implement the <NSCoding> protocol. When you archive aggregate objects (like NSArray and NSSet) each of the objects that are contained in the array or set need to implement the <NSCoding> protocol also. In this case PacsServer is being sent an encodeWithCoder: message which it doesn't implement.
See this tutorial on NSCoding.
2) Is it really a good idea to be persisting these objects in the NSUserDefaults? NSUserDefaults is really designed for small bits of information like whether you should show a help item, or is this the first time the user has launched your app. Perhaps you'd be better off saving this data in a separate .dat file or even using a different method like CoreData or sqlite (if you have a large amount of data).

Accessing NSDictionary in Singleton Class w/o Creating Copies

I have a singleton as follows, which creates an instance of NSDictionary to hold my data. Here is the .h:
#interface FirstLast : NSObject
#property (strong, nonatomic, readonly) NSArray *firstArray;
#property (strong, nonatomic, readonly) NSArray *lastArray;
#property (strong, nonatomic, readonly) NSDictionary *fl;
+ (FirstLast *) firstLast;
- (NSDictionary *) tempDic;
#end
Here is the .m
#implementation FirstLast
#synthesize firstArray = _firstArray;
#synthesize lastArray = _lastArray;
#synthesize fl = _fl;
+ (FirstLast *)firstLast {
static FirstLast *singleton;
static dispatch_once_t once;
dispatch_once(&once, ^{
singleton = [[FirstLast alloc] init];
NSLog(#"FirstLast instantiated");
});
return singleton;
}
- (NSDictionary *) tempDic{
_firstArray = [[NSArray alloc] initWithObjects:#"Bob", #"Joe", #"Sally", #"Sue", nil];
_lastArray = [[NSArray alloc] initWithObjects:#"Jones", #"Johnson", #"Thompson", #"Miller", nil];
_fl = [NSDictionary dictionaryWithObjects:_firstArray
forKeys:_lastArray];
NSLog(#"tempDic just made _fl at this address");
NSLog(#"%p", _fl);
return _fl;
}
#end
All of this works fine. In the view controller I instantiate all this for the first time (works fine too):
NSLog(#"VC is setting up tempDic");
[[WordsClues wordsClues] tempDic];
When I try to gain access to tempDic elsewhere, like this:
NSInteger rIndex = arc4random_uniform(4) + 1;
NSString *fname = [[[FirstLast firstLast].tempDic allValues] objectAtIndex:rIndex];
it works fine, but, when I repeat this process, each time I'm creating a new tempDic. I know this because the NSLog giving the address gives a different answer each time. I really want to access the existing dictionary, which is what I thought my singleton was going to accomplish. Clearly I'm either not accessing tempDic correctly or I misunderstand what the singleton can do for me or I have the tempDic set up wrong. The goal is to get a random value from a single copy of tempDic and not write local copies of tempDic all over the place. Thanks.
Why do you recreate the dictionary in -tempDic at all?
I.e. move the dictionary instantiation code to init and then just return _fl; in tempDic.
No worries -- we've all been there [new].
In your FirstLast class, implement the init method as something like:
- init
{
self = [super init];
if ( self ) {
_fl = ... create your dictionary here ...;
}
return self;
}
Then change -tempDic to:
- (NSDictionary*)tempDic {
return _fl;
}
I would highly recommend that you read a good intro to Objective-C book. I'm a purist and, thus, would recommend going to the source for the information, but there are lots of books available.
The questions you are asking are more in line with "What is object oriented programming and how does Objective-C work?".
To answer your question; FirstLast is a class and the singleton pattern makes sure there is exactly one instance of that class. By moving the creation of the dictionary to the init method -- which is called only once and who stores a reference to the created dictionary in an instance variable -- you avoid creating multiple dictionary instances.
Every time you call tempDic, you create a new copy of it. What you should do is add you code for creating the dictionary to your alloc instance, and then just retrieve it in your getter.
Alternativly you can do this
- (NSDictionary *) tempDic{
if( _fl == nil )
{
_firstArray = [[NSArray alloc] initWithObjects:#"Bob", #"Joe", #"Sally", #"Sue", nil];
_lastArray = [[NSArray alloc] initWithObjects:#"Jones", #"Johnson", #"Thompson", #"Miller", nil];
_fl = [NSDictionary dictionaryWithObjects:_firstArray
forKeys:_lastArray];
NSLog(#"tempDic just made _fl at this address");
NSLog(#"%p", _fl);
}
return _fl;
}

iOS - NSMutableArray shows objects out of bounds on setting property

I have implemented the following code to assign NSMutableArray to a property -
NSMutableArray * anArray = [responseDictionary valueForKeyPath:#"tags"];
NSLog(#"The array length is=%d",[anArray count]);
for (NSString *s in anArray) {
NSLog(#"you are %#", s);
}
[self setActiveTagArray:anArray];
It prints out the string values fine. But in the setter function, if I place a breakpoint I see that it shows there are two objects but they are "Out of Scope". What does this mean? What am I doing wrong? My getter also does not fetch any values. The property functions -
-(void)setActiveTagArray:(NSMutableArray *)tags
{
activeTagArray = [[NSMutableArray alloc] init];
activeTagArray = tags;
//NSLog(#"%#",[activeTagArray count]);
}
-(NSMutableArray *)getActiveTagArray
{
return activeTagArray;
}
Is activeTagArray a class variable as well as a property. Consider using _activeTagArray as the class variable name. And then in the .m file just use #synthesize activeTagArray = _activeTagArray;, and for get the second two methods completely.
Response to comment:
You said "I have implemented the following code to assign NSMutableArray to a property". I took this to mean you have "#property (nonatomic, retain) NSMutableArray *activeTagArray;" in your .h file. If this is the case then you would access it thru otherObject'sNameForYourClassHere.activeTagArray.
#synthesize create accessors & mutators for you.

Pass array from one Objective-C class to another

Im attempting to pass an array that is created in one class into another class. I can access the data but when I run count on it, it just tells me that I have 0 items inside the array.
This is where peopleArray's data is set up, it's in a different class than the code that is provided below.
[self setPeopleArray: mutableFetchResults];
for (NSString *existingItems in peopleArray) {
NSLog(#"Name : %#", [existingItems valueForKey:#"Name"]);
}
[peopleArray retain];
This is how I get the array from another class, but it always prints count = 0
int count = [[dataClass peopleArray] count];
NSLog(#"Number of items : %d", count);
The rest of my code:
data.h
#import <UIKit/UIKit.h>
#import "People.h"
#class rootViewController;
#interface data : UIView <UITextFieldDelegate>{
rootViewController *viewController;
UITextField *firstName;
UITextField *lastName;
UITextField *phone;
UIButton *saveButton;
NSMutableDictionary *savedData;
//Used for Core Data.
NSManagedObjectContext *managedObjectContext;
NSMutableArray *peopleArray;
}
#property (nonatomic, assign) rootViewController *viewController;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) NSMutableArray *peopleArray;
- (id)initWithFrame:(CGRect)frame viewController:(rootViewController *)aController;
- (void)setUpTextFields;
- (void)saveAndReturn:(id)sender;
- (void)fetchRecords;
#end
data.m(some of it at least)
#implementation data
#synthesize viewController, managedObjectContext, peopleArray;
- (void)fetchRecords {
[self setupContext];
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"People" inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Define how we will sort the records
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Name" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults) {
// Handle the error.
// This is a serious error and should advise the user to restart the application
}
// Save our fetched data to an array
[self setPeopleArray: mutableFetchResults];
for (NSString *existingItems in peopleArray) {
NSLog(#"Name : %#", [existingItems valueForKey:#"Name"]);
}
[peopleArray retain];
[mutableFetchResults release];
[request release];
//NSLog(#"this is an array: %#", eventArray);
}
login.h
#import <UIKit/UIKit.h>
#import "data.h"
#class rootViewController, data;
#interface login : UIView <UITextFieldDelegate>{
rootViewController *viewController;
UIButton *loginButton;
UIButton *newUser;
UITextField *entry;
data *dataClass;
}
#property (nonatomic, assign) rootViewController *viewController;
#property (nonatomic, assign) data *dataClass;
- (id)initWithFrame:(CGRect)frame viewController:(rootViewController *)aController;
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField;
#end
login.m
#import "login.h"
#import "data.h"
#interface login (PrivateMethods)
- (void)setUpFromTheStart;
- (void)loadDataScreen;
-(void)login;
#end
#implementation login
#synthesize viewController, dataClass;
-(void)login{
int count = [[dataClass peopleArray] count];
NSLog(#"Number of items : %d", count);
}
Is it the same object? If so, what you have should work. Check to see how you are getting the dataClass instance -- if you alloc a new one, you don't get the array from the other object.
Edit: From your comments below, it appears that you are having some confusion on the difference between classes and objects. I will try to explain (I'm going to simplify it):
A class is what you write in Xcode. It's the description that lets your application know how to create and access objects at run-time. It is used to figure out how much memory to allocate (based on instance variables) and what messages can be sent, and what code to call when they are. Classes are the blueprints for creating objects at runtime.
An object only exists at run-time. For a single class, many objects of that class can be created. Each is assigned its own memory and they are distinct from each other. If you set a property in one object, other objects don't change. When you send a message to an object, only the one you send it to receives it -- not all objects of the same class.
There are exceptions to this -- for example if you create class properties (with a + instead of a - at the beginning), then they are shared between all objects -- there is only one created in memory, and they all refer to the same one.
Also, since everything declared with a * is a pointer -- you could arrange for all pointer properties to point to the same data. The pointer itself is not shared.
Edit (based on more code): dataClass is nil, [dataClass peopleArray] is therefore nil, and then so is the count message call. You can send messages to nil, and not crash, but you don't get anything useful.
I don't see how the login object is created. When it is, you need to set its dataClass property.
Try running the code in the debugger, setting breakpoints, and looking at variables.
From the code, it looks like you are passing a mutable array.
[self setPeopleArray: mutableFetchResults];
Probably the items of the array are removed somewhere in your calling class / method. Or the array is reset by the class from which you get the mutableFetchResults in the first place.

NSMutableDictionary not retaining values

I am semi-new to Objective-c and confused with why my NSMutableDictionary is not retaining information. I am declaring my variable in the header file:
#interface view_searchResults : UIViewController <UITableViewDataSource, UITableViewDelegate> {
NSMutableDictionary *imageDicationary;
}
#property (nonatomic, retain) NSMutableDictionary *imageDictionary;
Then in my .m file, I have the following:
#synthesize imageDictionary;
-(UIImage *)getImageForURL:(NSURL*)url {
UIImage*image;
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
[imageDictionary setObject:image forKey:#"test"];
if([imageDictionary objectForKey:#"test"]){
NSLog(#"Exists");
}
}
There is obviously other code to support this, but I can confirm that a URL is being passed, and the file is downloading correctly elsewhere. Also, I can confirm that this function is being executed, and I am not referring to the NSMutableDictionary anywhere else in the document.
Thanks!
Where do you create your NSMutable dictionary? If this really is all the code you have you need to create the dictionary:
#implementation view_searchResults
- (id) init;{
self = [super init];
if(self) {
imageDicationary = [NSMutableDictionary alloc] init]; // should also be released in dealloc.
}
return self;
}
If this is the error then the reason you are not causing a crash is because in objective-C it is valid to send a message to the nil object - it just does nothing.
You havent told us whether the "Exists" NSLog is executed, you also are NOT returning the image.
In other words, I fail to see your problem
Has imageDictionary been initialized? (alloc/init?)