Short Version:
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.
Long Version:
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.
You must use the accessor:
or (is equivalent to):
instead of
which only sets the ivar without any notion of property.