jcalonso
jcalonso

Reputation: 1503

Using NSUserDefaults to save an Array

I was following the answer for this question (How To Save Data For Bookmarks?) to save a bookmarks into the NSUserDefaults. It is pretty straightforward, and it works properly for the first time (when there's no entry in the plist). But then it tries to copy the previously saved bookmarks into the new NSMutableArray the app crashes:

 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: '*** -[NSMutableArray initWithCapacity:]: 
method only defined for abstract class.  Define -[__NSArrayM initWithCapacity:]!'

This is my code (a little modified):

-(void) addStationToFavorites{

    // Get the user defaults object
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    NSString *userSelectedService = [userDefaults objectForKey:@"idBikeServiceProvider"];

    // Load your bookmarks (editable array)
    NSMutableArray *bookmarks = [[NSMutableArray alloc] initWithCapacity:100];
    NSArray *bookmarksLoaded = [userDefaults arrayForKey:[NSString stringWithFormat: @"favStationForService%@",userSelectedService]];
    if (bookmarksLoaded != nil) {
        [bookmarks initWithArray:bookmarksLoaded];
    } else {
        [bookmarks init];
    }

    // Add a bookmark
    NSMutableDictionary *bookmark = [NSMutableDictionary new];
    [bookmark setValue:pass_stationName forKey:pass_stationId];
    [bookmarks addObject:bookmark];

    // Save your (updated) bookmarks
    [userDefaults setObject:bookmarks forKey:[NSString stringWithFormat: @"favStationForService%@",userSelectedService]];
    [userDefaults synchronize];

    // Memory cleanup
    [bookmarks release];

    //Show feedback
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@""
                                                    message:@"Added!"
                                                   delegate:self
                                          cancelButtonTitle:@"Ok"
                                          otherButtonTitles:nil];
    [alert show];
    [alert release];

}

Upvotes: 1

Views: 3128

Answers (2)

benzado
benzado

Reputation: 84378

This is incorrect:

// Load your bookmarks (editable array)
NSMutableArray *bookmarks = [[NSMutableArray alloc]initWithCapacity:100]; // OK
NSArray *bookmarksLoaded = [userDefaults arrayForKey:[NSString stringWithFormat: @"favStationForService%@",userSelectedService]];
if (bookmarksLoaded != nil) {
    [bookmarks initWithArray:bookmarksLoaded]; // NOT AGAIN
} else {
    [bookmarks init]; // NOT AGAIN
}

Don't call an init method more than once on an object. I suspect that is the source of your error, as NSArray/NSMutableArray is implemented as a "class cluster" and the exact class that is returned by the initWithCapacity: is not NSMutableArray, but rather some private subclass, which is complaining about being init'd a second time. Here's a better way to accomplish the same:

// Load your bookmarks (editable array)
NSMutableArray *bookmarks;
NSArray *bookmarksLoaded = [userDefaults arrayForKey:[NSString stringWithFormat: @"favStationForService%@",userSelectedService]];
if (bookmarksLoaded != nil) {
    bookmarks = [bookmarksLoaded mutableCopy];
} else {
    bookmarks = [[NSMutableArray alloc] initWithCapacity:100];
}

The mutableCopy method is very useful when you need a mutable version of an immutable container object. Note that, since "copy" is in the method name, you own the result and must release it later (unless you're using ARC or Garbage Collection and don't need to worry about it).

Upvotes: 3

James
James

Reputation: 2376

Things returned from NSUserDefaults are always immutable, regardless of whether they were mutable before you stored them.

initWithCapacity:

is only defined on NSMutableArray and not on NSArray.

Edit: It looks like you're also initializing your object twice:

// Load your bookmarks (editable array)
    NSMutableArray *bookmarks = [[NSMutableArray alloc]initWithCapacity:100];
    NSArray *bookmarksLoaded = [userDefaults arrayForKey:[NSString stringWithFormat: @"favStationForService%@",userSelectedService]];
    if (bookmarksLoaded != nil) {
        [bookmarks initWithArray:bookmarksLoaded];

Upvotes: 2

Related Questions