ARC `BAD_ACCESS` when using category method of NSString
cocoa-touch, exc-bad-access, ios, iphone, objective-c
Solution
As others have stated the problem lies in the way you retrieve objects from the variable argument list which might not be compatible with ARC.
`va_arg` has a funny way of how to return a value of a specific type which ARC is probably not aware of. I'm not sure if this is a bug in clang or if it is the intended behavior for ARC. I'll clarify this issue and will update the post accordingly.
As a workaround just avoid the problem by using void pointers in the argument handling and convert them to objects properly in an ARC safe way:
for (NSString *key = firstKey; key != nil; key = (__bridge NSString *)va_arg(_arguments, void *)) {
NSString *value = (__bridge NSString *)va_arg(_arguments, void *);
NSAssert(value != NULL, @"Premature occurence of nil.");
result = [result stringByReplacingToken:key
withString:value];
}
Edit: The __bridge cast tells ARC to not do something about ownership. It just expects the object to be alive and does not transfer or give up ownership. Nevertheless the `key` and `value` variables maintain strong references to the objects while in use.
Second Edit: It seems that clang/ARC should be aware of the type in va_arg and either warn or just do the right thing (see this, for example).
I tried to reproduce your problem without success. Everything works for me on:
$ clang --version
> Apple clang version 4.0 (tags/Apple/clang-421.10.48) (based on LLVM 3.1svn)
Which Xcode version do you use?
Problem
I call a utility method of mine like so: ``` NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; [dateFormat setDateFormat:@"dd.MM.yy HH:mm"]; NSString *dateString = [dateFormat stringFromDate:[NSDate date]]; return [[Environment sharedInstance].versionLabelFormat replaceTokensWithStrings: @"VERSION", APP_VERSION, @"BUILD", APP_BULD_NUMBER, @"DATETIME" , dateString, nil ]; ``` This is the `NSString` category method ``` -(NSString *)replaceTokensWithStrings:(NSString *)firstKey, ... NS_REQUIRES_NIL_TERMINATION{ NSString *result = self; va_list _arguments; va_start(_arguments, firstKey); for (NSString *key = firstKey; key != nil; key = va_arg(_arguments, NSString*)) { // The value has to be copied to prevent crashes NSString *value = [(NSString *)(va_arg(_arguments, NSString*))copy]; if(!value){ // Every key has to have a value pair otherwise the replacement is invalid and nil is returned NSLog(@"Premature occurence of nil. Each token must be accompanied by a value: %@", result); return nil; } result = [result replaceToken:key withString:value]; } va_end(_arguments); // Check if there are any tokens which were not yet replaced (for example if one value was nil) if([result rangeOfString:@"{"].location == NSNotFound){ return result; } else { NSLog(@"Failed to replace tokens failed string still contains tokens: %@", result); return nil; } } ``` No on the following line I had to add a `copy` statement otherwise there would be a Zombie with the `dateString`: ``` NSString *value = [(NSString *)(va_arg(_arguments, NSString*))copy]; ``` To be more specific the Zombie Report told me this: ``` 1 Malloc NSDateFormatter stringForObjectValue: Autorelease NSDateFormatter stringForObjectValue: 2 CFRetain MyClass versionString: 3 CFRetain replaceToken:withString: 2 CFRelease replaceToken:withString: 1 CFRelease replaceTokensWithStrings: ( One release too much!) 0 CFRelease MyClass versionString: -1 Zombie GSEventRunModal ``` Although the `copy` statement seems to fix the problem I would like to understand what is not ARC-complient with the code so that the `BAD_ACCESS` would occur without the `copy` for the value string.