Reputation: 1503
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
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
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