¿Por qué tengo que retener una propiedad a pesar de usar (no atómico, retener)?

Version corta:

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.

Versión larga:

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];
}

Esto arroja un EXEC BAD ACCESS error en 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.

preguntado el 03 de mayo de 12 a las 10:05

2 Respuestas

You must use the accessor:

self.gameState = [NSMutableDictionary dictionaryWithContentsOfFile:sourcePath];

or (is equivalent to):

[self setGameState:[NSMutableDictionary dictionaryWithContentsOfFile:sourcePath]];

en lugar de

gameState = [NSMutableDictionary dictionaryWithContentsOfFile:sourcePath];

which only sets the ivar without any notion of property.

contestado el 03 de mayo de 12 a las 11:05

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!

contestado el 03 de mayo de 12 a las 11:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.