Reputation: 8946
I define a property with (nonatomic, retain)
and assumed that the property would be retained. But unless I call retain
when assigning a dictionary to the property, The app crashes with an EXEC BAD ACCESS
error.
I have a singleton which has a dictionary. The header is defined like this
@interface BRManager : NSObject {
}
@property (nonatomic, retain) NSMutableDictionary *gameState;
+ (id)sharedManager;
- (void) saveGameState;
@end
In the implementation file, I have a method that's called in the init. This method loads a plist form the bundle and makes a copy of it in the users documents folder on the device.
- (void) loadGameState
{
NSFileManager *fileManger=[NSFileManager defaultManager];
NSError *error;
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *doumentDirectoryPath=[pathsArray objectAtIndex:0];
NSString *destinationPath= [doumentDirectoryPath stringByAppendingPathComponent:@"gameState.plist"];
NSLog(@"plist path %@",destinationPath);
if (![fileManger fileExistsAtPath:destinationPath]){
NSString *sourcePath=[[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:@"gameStateTemplate.plist"];
[fileManger copyItemAtPath:sourcePath toPath:destinationPath error:&error];
gameState = [NSMutableDictionary dictionaryWithContentsOfFile:sourcePath];
}else{
gameState = [NSMutableDictionary dictionaryWithContentsOfFile:destinationPath];
}
}
Now here's how I thought this should work. In the header I define the gameState property with (nonatomic, retain). I assumed (probably incorrectly) that 'retain' meant that the gameState dictionary would be retained. However, I have another method in my singleton (saveGameState) that get's called when the AppDelegate -> 'applicationWillResignActive
'.
- (void) saveGameState
{
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *doumentDirectoryPath=[pathsArray objectAtIndex:0];
NSString *plistPath = [doumentDirectoryPath stringByAppendingPathComponent:@"gameState.plist"];
[gameState writeToFile:plistPath atomically:YES];
}
This throws an EXEC BAD ACCESS
error on gameState
. If I modify loadGameState to retain the gameState dictionary, everything works as it should. eg:
gameState = [[NSMutableDictionary dictionaryWithContentsOfFile:sourcePath] retain];
I'm guessing this is the correct behaviour, but why? Does (nonatomic, retain)
not mean what I think it means, or is something else at play here?
I've not really grok'd memory management yet, so I stumble on this stuff all the time.
Upvotes: 1
Views: 163
Reputation: 9913
Where do you declare gameState as an ivar? I'm presuming you do so in the implementation.
The real problem is that in your implementation, you access gameState directly and don't actually invoke the property you've declared. To do so you must send self the appropriate message:
[self gameState]; // invokes the synthesized getter
[self setGameState:[NSMutableDictionary dictionaryWithContentsOfFile:sourcePath]]; // invokes the synthesized setter -- solves your problem
or
whatever = self.gameState; // invokes the getter
self.gameState = [NSMutableDictionary dictionaryWithContentsOfFile:sourcePath]; // invokes the synthesized setter -- solves your problem
Make sure you get around to groking that memory management literature... this is a very basic question which, according to the strict rules of StackOverflow, I shouldn't be answering. Good luck!
Upvotes: 0
Reputation: 16827
You must use the accessor:
self.gameState = [NSMutableDictionary dictionaryWithContentsOfFile:sourcePath];
or (is equivalent to):
[self setGameState:[NSMutableDictionary dictionaryWithContentsOfFile:sourcePath]];
instead of
gameState = [NSMutableDictionary dictionaryWithContentsOfFile:sourcePath];
which only sets the ivar without any notion of property.
Upvotes: 6