iosautomatic-ref-countingautoreleasensautoreleasepool

Will an object returned by a method be put into autorelease pool?


When ARC is enabled, will o be put into autorelease pool in this code snippet?

- (NSObject *)obj {
    NSObject *o = [[NSObject alloc] init];

    return o;
}

What's more, what is the difference between these two code snippets?

- (NSObject *)obj {
    NSObject * __autoreleasing o = [[NSObject alloc] init];

    return o;
}

vs.

- (NSObject *)obj {
    NSObject * __strong o = [[NSObject alloc] init];

    return o;
}

Solution

  • When ARC is enabled, will o be put into autorelease pool in this code snippet?

    The answer is, it may, or it may not. ARC doesn't make a guarantee either way. Here the method's name (obj) does not start with one of the special names that indicate a retained return type (e.g. alloc, retain, new, copy, mutableCopy), so it returns a non-retained reference. The ARC specification section on "Unretained return values" says:

    In the worst case, this may involve an autorelease, but callers must not assume that the value is actually in the autorelease pool.

    In the old days, under MRC, you had to do autorelease it in this case before you return, because the method is required to return a non-retained reference, but someone must hold a strong reference to the object or else it will be deallocated. Since the object was created in your method, nobody else has a reference to it, and the method must get rid of its strong reference at the end of its scope, since it cannot return a retained reference, so the only way was to have the autorelease pool hold a strong reference across the return.

    ARC must remain ABI-compatible with MRC (i.e. you should be able to replace MRC implementation with ARC, or vice versa, without changing the headers and callers don't need to know whether it was compiled with ARC or MRC to work correctly). Therefore, in the case where your method is called from MRC code, following the same logic as in the above paragraph, your method must autorelease; there is simply no other way to do it.

    However, ARC introduces a clever optional runtime optimization that can eliminate the autorelease in some cases when both the caller and callee are compiled with ARC, without the caller or callee needing to know about the other side. This is approximately how it works: In the return of a function, where you would normally autorelease, ARC instead calls objc_autoreleaseReturnValue(). And in code that calls a function and retains the return value, it instead calls objc_retainAutoreleasedReturnValue(). In objc_autoreleaseReturnValue(), it looks at the return address up the stack frame to see whether the returned value will be passed to objc_retainAutoreleasedReturnValue(). If not, it will autorelease. If it is, then it will skip the autorelease, and also modify the return address to skip the objc_retainAutoreleasedReturnValue, thus removing both an autorelease and a retain, which cancels out. In objc_retainAutoreleasedReturnValue(), if it is not skipped over, it will just retain. This works correctly for MRC-to-ARC calls, ARC-to-ARC calls, and ARC-to-MRC calls, and will eliminate autorelease for some ARC-to-ARC calls.


    What's more, what is the difference between these two code snippets?

    The first will put it into the autorelease pool, while the second may not. (By the way, __strong is the default, so the third piece of code is identical to the first.)

    But there is essentially no good reason to use __autoreleasing for a local variable instead of the default (which is __strong); __strong is just simpler and easier to think about.

    Usually the only time you will encounter __autoreleasing is in "pointer-to" types in parameters -- if a function declares it takes a pointer to __autoreleasing (e.g. NSObject * __autoreleasing *), it means that it will write to the pointed-to variable in a different way than if it takes a pointer to __strong (e.g. NSObject * __strong *). Both ways work, but the caller and callee need to agree on which one. Because of the ubiquitous use of NSError * __autoreleasing * in Cocoa, an unqualified one defaults to pointer to __autoreleasing (e.g. NSObject ** means NSObject * __autoreleasing *).