On May 24, 2014, at 5:04 PM, Jens Alfke <[email protected]> wrote:
> On May 24, 2014, at 2:34 PM, Jamie Ojomoh <[email protected]> wrote:
>
>> In the example, everything inside the autoreleasepool block will be
>> released as soon as the block ends, so it's necessary to declare the return
>> value outside the block.
>
> No, in general ARC understands that the return value needs to keep a
> reference, so it’s safe to put the return statement inside the autorelease
> block. In your specific example, returnString was allocated before you
> created your autorelease pool, so that pool won’t release it anyway. Also,
> you allocated returnString using an alloc/init sequence so it’s not in an
> autorelease pool at all.
This isn't strictly true; when you are returning objects by reference, doing so
inside the @autoreleasepool will cause a crash. For example:
#import <Foundation/Foundation.h>
static BOOL DoSomethingElse(NSError *__autoreleasing *error) {
if (error) *error = [[NSError alloc] initWithDomain:@"Foo" code:-1
userInfo:nil];
return NO;
}
static BOOL DoSomething(NSError *__autoreleasing *error) {
@autoreleasepool {
return DoSomethingElse(error);
}
}
int main(int argc, const char * argv[]){
@autoreleasepool {
NSError *error = nil;
if (DoSomething(&error)) {
NSLog(@"Success");
} else {
NSLog(@"Error: %@", error); // crashes here
}
}
return 0;
}
Interestingly, this still happens even if you declare the NSError variable out
of the @autoreleasepool block:
#import <Foundation/Foundation.h>
static BOOL DoSomethingElse(NSError *__autoreleasing *error) {
if (error) *error = [[NSError alloc] initWithDomain:@"Foo" code:-1
userInfo:nil];
return NO;
}
static BOOL DoSomething(NSError *__autoreleasing *error) {
NSError *theError = nil;
@autoreleasepool {
if (DoSomethingElse(&theError)) {
return YES;
} else {
if (error) *error = theError;
return NO;
}
}
}
int main(int argc, const char * argv[]){
@autoreleasepool {
NSError *error = nil;
if (DoSomething(&error)) {
NSLog(@"Success");
} else {
NSLog(@"Error: %@", error); // still crashes here
}
}
return 0;
}
The only thing that avoids the crash is returning outside of the
@autoreleasepool block:
#import <Foundation/Foundation.h>
static BOOL DoSomethingElse(NSError *__autoreleasing *error) {
if (error) *error = [[NSError alloc] initWithDomain:@"Foo" code:-1
userInfo:nil];
return NO;
}
static BOOL DoSomething(NSError *__autoreleasing *error) {
BOOL success;
NSError *theError = nil;
@autoreleasepool {
success = DoSomethingElse(&theError);
}
if (success) {
return YES;
} else {
if (error) *error = theError;
return NO;
}
}
int main(int argc, const char * argv[]){
@autoreleasepool {
NSError *error = nil;
if (DoSomething(&error)) {
NSLog(@"Success");
} else {
NSLog(@"Error: %@", error); // now this actually works
}
}
return 0;
}
The thing that makes this really insidious is that you only get the crash when
an error occurs, so if you have an error case that only happens a tiny
percentage of the time, it can slip through your testing.
Charles
_______________________________________________
Cocoa-dev mailing list ([email protected])
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com
This email sent to [email protected]