Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need more explanation on usage of __autoreleasing

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

like image 775
Andrey Chernukha Avatar asked Jan 06 '14 12:01

Andrey Chernukha


1 Answers

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.

like image 170
JeremyP Avatar answered Nov 06 '22 01:11

JeremyP