On May 24, 2014, at 5:04 PM, Jens Alfke <j...@mooseyard.com> wrote:

> On May 24, 2014, at 2:34 PM, Jamie Ojomoh <jamie.ojo...@gmail.com> 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 (Cocoa-dev@lists.apple.com)

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 arch...@mail-archive.com

Reply via email to