Organizational Research By

Surprising Reserch Topic

Question:Retrieving and re-saving an nsmutablearray to nsuserdefaults from a uitableview


I have a uitableview with a list of items populated from a JSON file located locally within the app. Everything works as far as getting the list to the table and the multiple selection of items which when selected (or deselected) are then saved to a nsmutablearray.

The problem is when the user leaves the view and returns and selects another item (or deselects a currently selected item). At this point the mutable array is then empty.

I'm not sure if the nsuserdefaults saving of the mutable array is the problem. it saves it fine but then when the view reappears (the mutable array's value is fine at this point) and the user touches a table row the array is null once more.

my .h file:

@interface CategoriesViewController : UITableViewController {

    NSMutableArray *_selectedItems;

    NSString *filePath;

    NSString *string;

}

// arForTable array will hold the JSON results from the api
@property (nonatomic, retain) NSArray *arForTable;

@property (nonatomic, retain) NSMutableArray *categorySelected;

@property (nonatomic, retain) NSString *jsonStringCategory;
@property(nonatomic, retain) UIView *accessoryView;

@end

my .m file:

@implementation CategoriesViewController
@synthesize arForTable = _arForTable;

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.categorySelected = [[NSMutableArray alloc] init];

    [self reloadMain];

    // assignment reference so don't release
     _selectedItems = [(AppDelegate *)[[UIApplication sharedApplication] delegate] selectedCategories];

    self.tableView.hidden = NO;

}

-(void) reloadMain {

    // countrySaved value from NSUserDefaults
    NSUserDefaults * defaults =  [NSUserDefaults standardUserDefaults];

    NSString *countryString = [defaults stringForKey:@"selectedCountryTableString"];
    NSString *cityString = [defaults stringForKey:@"selectedCityTableString"];
    NSLog(@"countrystring from category is %@", countryString);
    NSLog(@"citystring from category is %@", cityString);

    // getting path to the file

    if ([defaults stringForKey:@"selectedCountryTableString"] == NULL) {

        filePath = [[NSBundle mainBundle] pathForResource:@"categoriesit" ofType:@"json"];

    } else if ([countryString isE

asked Sep 13, 2013 in Java Interview Questions by rajesh
recategorized Sep 12, 2013 by rajesh
0 votes
101 views



Related Hot Questions

4 Answers

0 votes
First serialize the array when leaving the view:

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:@[@"1",@"2",@"3"]];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"myarray"];
[[NSUserDefaults standardUserDefaults] synchronize];

Then deserialize it when you are back to that view:

NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"myarray"];
NSArray *myarray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"MYARRAY %@", myarray);

Note: if key is not found then init a new array.
answered Sep 13, 2013 by rajesh
edited Sep 12, 2013
0 votes

edit

// DONT EVER EVER EVER EVER EVER EVER DO THIS!!!
//  We don't use types as variable names, that is implicit...
//  I get it, this is a string, BUT WHAT IS IT A STRING OF, the name
//  'string' does you, and anyone else, no good.  Think about all your 
//  code like you are writing it for someone else, because when you come
//  back to it in 6 months, you will be someone else, and you won't know
//  what this means
NSString *string;

end edit

I would not be using NSUserDefaults this way. You have already parsed JSON into an archiveable object (NSMutableArray). In viewDidLoad, you should probably try doing something like:

-(void)viewDidLoad
{
     // Load the array from a plist file
     self.dataYouNeed = [NSMutableArray arrayWithContentsOfFile:@"someFileName.plist"];
     // If we got back nil, that file didn't exist, so call 'reloadMain',
     //  do your parsing there THEN SAVE to a plist using:
     //
     // [myArray writeToFile:@"someFileName.plist"]
     //
     if(self.dataYouNeed == nil) [self reloadMain];

     // Then do the exact same thing when you try to persist your selection...
     //  aka do not store a CSV string, just store an Array, and call writeToFile:
     //  when you want to save, and arrayWithContentsOfFile when you want to read 
     //  it back in

}

On top of that, depending on where your data is coming from, I would move all of your data out of the JSON files and set it up in a plist, then you can ditch all of your parsing code.... :). Basically I am saying this is all a little too complicated for such a simple task, make your own life easier.

edit

You may have an issue with not using 'self.string', simply referring to 'string' is dangerous, you are creating a new reference everytime. This is most likely creating a memory leak. (ARC is not magic, it can not handle ALL memory management for you)

edit

Ok, so re-reading your code, I noticed a few things.

1. Why do you store your CSV string in the 'string' instance var?

This is somewhat redundant. At no point do you ever read from this variable without having set it in the few lines of code before. It should just be an NSString declared with in the scope of the method.

2. Are you expecting '_selectedItems' to have retained your reference to the 'selectedCategories' array on your AppDelegate?

You can not make this assumption, especially without having made a @property declaration. ARC does not know how to handle it and will probably be releasing the reference when you leave the view. The more likely possibility is that you are creating a memory leak every time you set that variable. You can also not guarantee that viewDidLoad will be called again to reset the reference. You should probably be setting this in viewWillAppear.

3. Which NSMutableArray are you experiencing a nil reference to?

If it is '_selectedItems', consider #2. If it is 'categorySelected', this is also probably being released when this view disappears. If this is really what you are trying to persist, then why are you not populating it from the viewDidAppear method. The only thing you do in viewDidAppear is set the 'string' variable (which is never actually read from, like #1 says). Did you mean to set 'categorySelected' here? I believe you meant to get your list from NSUserDefaults, then populate 'categorySelected' using that string's componentsSeparatedByString: method, which returns an array

 

answered Sep 13, 2013 by rajesh
edited Sep 12, 2013
0 votes
First serialize the array when leaving the view:

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:@[@"1",@"2",@"3"]];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"myarray"];
[[NSUserDefaults standardUserDefaults] synchronize];

Then deserialize it when you are back to that view:

NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"myarray"];
NSArray *myarray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"MYARRAY %@", myarray);

Note: if key is not found then init a new array.
answered Sep 13, 2013 by rajesh
edited Sep 12, 2013
0 votes

edit

// DONT EVER EVER EVER EVER EVER EVER DO THIS!!!
//  We don't use types as variable names, that is implicit...
//  I get it, this is a string, BUT WHAT IS IT A STRING OF, the name
//  'string' does you, and anyone else, no good.  Think about all your 
//  code like you are writing it for someone else, because when you come
//  back to it in 6 months, you will be someone else, and you won't know
//  what this means
NSString *string;

end edit

I would not be using NSUserDefaults this way. You have already parsed JSON into an archiveable object (NSMutableArray). In viewDidLoad, you should probably try doing something like:

-(void)viewDidLoad
{
     // Load the array from a plist file
     self.dataYouNeed = [NSMutableArray arrayWithContentsOfFile:@"someFileName.plist"];
     // If we got back nil, that file didn't exist, so call 'reloadMain',
     //  do your parsing there THEN SAVE to a plist using:
     //
     // [myArray writeToFile:@"someFileName.plist"]
     //
     if(self.dataYouNeed == nil) [self reloadMain];

     // Then do the exact same thing when you try to persist your selection...
     //  aka do not store a CSV string, just store an Array, and call writeToFile:
     //  when you want to save, and arrayWithContentsOfFile when you want to read 
     //  it back in

}

On top of that, depending on where your data is coming from, I would move all of your data out of the JSON files and set it up in a plist, then you can ditch all of your parsing code.... :). Basically I am saying this is all a little too complicated for such a simple task, make your own life easier.

edit

You may have an issue with not using 'self.string', simply referring to 'string' is dangerous, you are creating a new reference everytime. This is most likely creating a memory leak. (ARC is not magic, it can not handle ALL memory management for you)

edit

Ok, so re-reading your code, I noticed a few things.

1. Why do you store your CSV string in the 'string' instance var?

This is somewhat redundant. At no point do you ever read from this variable without having set it in the few lines of code before. It should just be an NSString declared with in the scope of the method.

2. Are you expecting '_selectedItems' to have retained your reference to the 'selectedCategories' array on your AppDelegate?

You can not make this assumption, especially without having made a @property declaration. ARC does not know how to handle it and will probably be releasing the reference when you leave the view. The more likely possibility is that you are creating a memory leak every time you set that variable. You can also not guarantee that viewDidLoad will be called again to reset the reference. You should probably be setting this in viewWillAppear.

3. Which NSMutableArray are you experiencing a nil reference to?

If it is '_selectedItems', consider #2. If it is 'categorySelected', this is also probably being released when this view disappears. If this is really what you are trying to persist, then why are you not populating it from the viewDidAppear method. The only thing you do in viewDidAppear is set the 'string' variable (which is never actually read from, like #1 says). Did you mean to set 'categorySelected' here? I believe you meant to get your list from NSUserDefaults, then populate 'categorySelected' using that string's componentsSeparatedByString: method, which returns an array

 

answered Sep 13, 2013 by rajesh
edited Sep 12, 2013

...