I'm desperately trying to understand the usage of __autoreleasing
keyword in Objective-C
. I have thoroughly read answers to the following questions:
In which situations do we need to write the __autoreleasing ownership qualifier under ARC?
Use of __autoreleasing in code snippet example
NSError and __autoreleasing
and despite now i understand more i still can't get the main thing, the purpose. Why do that? Let me explain what exactly confuses me. Consider the code:
@interface MyError : NSError
@end
@implementation MyError
- (void)dealloc
{
NSLog(@"My error dealloc");
}
@end
@interface ErrorHandler : NSObject
- (void)handleError:(MyError* __strong*)error;
@end
@implementation ErrorHandler
- (void)handleError:(MyError* __strong*)error
{
*error = [[MyError alloc] initWithDomain:@"Blabla" code:100500 userInfo:@{
NSLocalizedDescriptionKey : @"TestDescription"
}];
}
@end
- (void)test
{
MyError *error = nil;
ErrorHandler *handler = [ErrorHandler new];
[handler handleError:&error];
NSLog(@"Localized description %@", error.localizedDescription);
}
I wrote this code to see what happens if i don't use __autoreleasing
. As you see handleError
method accepts a reference to a reference which is explicitly declared as __strong
. And nothing happens. Everything is ok. I'm able to get the info from the MyError
object and it has been successfully deallocated, i see that. If i put __autoreleasing
instead of __strong
nothing changes. So why use __autoreleasing
if it changes nothing? This is what i don't understand. Can anyone show me what i'm missing? Thanks everyone
I think the tl;dr answer is that by declaring the argument as __autoreleasing
you can also pass a pointer to a weak reference if you want to.
Imagine your method looks like this:
-(void) handleError: (NSError* __strong *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
}
The compiler thinks that *error
is strong, so when it does the assignment, you get a +1 retain count. After the compiler has been over the code, it looks like this:
-(void) handleError: (NSError* __strong *) error
{
NSError* myError = [[NSError alloc] init];
*error = [myError retain];
[myError release];
}
So if you call it like this:
NSError* error; // strong reference
[self handleError: &error];
it's all fine because the compiler will put the release in correctly at the end of the scope. If you do this though:
NSError* __weak error; // weak reference
[self handleError: &error];
it probably won't compile, but if it did, because the compiler thinks the reference is weak (it can't see the strong assignment in handleError:
), it will not put in a release and the object will leak.
If you define the method like this:
-(void) handleError: (NSError* __weak *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
}
The compiler adds code so it looks like this:
-(void) handleError: (NSError* __weak *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
[myError release];
}
it's even worse because the assignment of *error
gives a +0 retain count which means that, as soon as myError
goes out of scope i.e. when the method returns, the object to which it points gets deallocated.
If you do this:
-(void) handleError: (NSError* __autoreleasing *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
}
The compiler does this:
-(void) handleError: (NSError* __autoreleasing *) error
{
NSError* myError = [[NSError alloc] init];
*error = [[myError retain] autorelease];
[myError release];
}
which is the same as the previous case except, the returned object is in an autorelease pool at the end of the method, so it won't get deallocated. So you can declare error
in the caller as either strong or weak and the compiler has the chance to do something sensible with the returned reference.
At least I think that's what the issue is.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With