iosobjective-cselectorexc-bad-accessnsinvocation

NSInvocation to delay calling of method causing EXC_BAD_ACCESS


I'm trying to display a message at the end of a game that shows whether or not the player has won.

Here is the relevant code:

BOOL yes = YES;
NSString *winMessage = [NSString stringWithFormat:@"You win!"];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(endGameWithMessage:win:par:)]];
[inv setSelector:@selector(endGameWithMessage:win:par:)];
[inv setTarget:self];
[inv setArgument:&winMessage atIndex:2];
[inv setArgument:&yes atIndex:3]; //this is the win BOOL (0 and 1 are explained in the link above)
[inv setArgument:&parBool atIndex:4]; //this is the par BOOL
[inv performSelector:@selector(invoke) withObject:nil afterDelay:0.1f];

And here is the endGameWithMessage method signature:

- (void)endGameWithMessage:(NSString*)message win:(BOOL)win par:(BOOL)parBool

Although calling the code directly (which obviously won't allow a delay) like so works just fine, with the message showing up as expected and not causing any crashes:

[self endGameWithMessage:@"You win!" win:YES par:parBool];

Attempting to use NSInvocation results in an EXC_BAD_ACCESS crash on the endGameWithMessage: method. Does this mean that I'm passing my values to the method invocation in the wrong manner?


Solution

  • There are two differences between those two pieces of code:

    1. One uses the string literal @"You win!", the other creates a new string object. String literals are allocated statically, and not memory-managed, so any incorrect memory management with the string parameter will affect the allocated object and not the string literal.

    2. More importantly, the NSInvocation is invoked asynchronously, and you didn't ask the invocation to retain arguments with [inv retainArguments];. That means self and winMessage are not retained by the invocation, as it should in order to use them asynchronously.