Re: Understanding ARC
Re: Understanding ARC
- Subject: Re: Understanding ARC
- From: Charles Srstka <email@hidden>
- Date: Sun, 25 May 2014 01:34:16 -0500
On May 24, 2014, at 5:04 PM, Jens Alfke <email@hidden> wrote:
> On May 24, 2014, at 2:34 PM, Jamie Ojomoh <email@hidden> 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@hidden)
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:
This email sent to email@hidden