Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the advantages of declaring method arguments __autoreleasing?

As per Transitioning to ARC Release Notes:

__autoreleasing is used to denote arguments that are passed by reference (id *) and are autoreleased on return.

For example:

-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;

But what are the advantages of the above comparing to:

-(BOOL)performOperationWithError:(NSError * __strong *)error;


Update:

Several answers refer to the temp var trick compiler does to deal with the mismatch between var and argument as the advantage of __autoreleasing. I don't see why compiler can not do the same trick for __strong argument. I mean, for a __weak var and __strong argument, compiler can similarly do this:

NSError * __weak error;
NSError * __strong tmp = error;
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;
if (!OK) {
    // Report the error.
}

Compiler knows -(BOOL)performOperationWithError:(NSError * __strong *)error; returns a strong reference(+1) so it handles it just like any new-family method. Since tmp lives in the same scope as error, compiler can reasonably keep it alive as long as error so the __weak reference(error) is now supported by a __strong reference(tmp) and will not be nullified until the end of the scope.

like image 888
an0 Avatar asked Jan 28 '13 01:01

an0


3 Answers

tl;dr

Implicitly converting a __weak object to a __strong object in this case would alter the semantic of the program, something that a compiler should never do.


The scenario

Let's take an example

NSError *error;
BOOL success = [myObject performOperationWithError:&error];
if (!success) {
    // Report the error
}

In such a case the error local variable is automatically inferred by ARC as __strong.

At the same time the error argument of

-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;

is of type NSError * __autoreleasing *.

Please note that in any case ARC will infer parameters passed by reference (id *) as being of type id __autoreleasing *, so the above signature is equivalent to

-(BOOL)performOperationWithError:(NSError **)error;

under ARC.

Therefore we have a mismatch since we are passing a __strong annotated variable to a method expecting an __autoreleasing argument.

Under the hood

In our example then the compiler will address such mismatch by creating a local __autoreleasing tmp variable.

The code becomes

NSError * __strong error;
NSError * __autoreleasing tmp = error;
BOOL success = [myObject performOperationWithError:&tmp];
error = tmp;
if (!success) {
    // Report the error.
}

An alternative

Let's now pretend that we can change the signature of performOperationWithError:.

If we want to avoid the "compiler trick" which uses the tmp variable, we can declare our signature as

-(BOOL)performOperationWithError:(NSError * __strong *)error;

We have a __strong variable and we are now passing it to a method expecting a __strong argument, so we just eliminated the mismatch.

Looks good, why not always declare __strong arguments?

One reason is that declaring the argument as __autoreleasing will make the method to accept even a __weak reference.

It does not make much sense in the current example, but there could be cases in which we'd like to pass a __weak variable by reference and declaring __autoreleasing (or leaving the ARC to infer it) will allow us to do so.

ARC will apply the same trick seen above, creating a __autoreleasing tmp variable.

Conclusion

The mechanism presented so far goes under the name of pass-by-writeback.

Such mechanism has been designed to work with __autoreleasing, __strong and __weak variables, so that the programmer can safely rely on the type inference made by the compiler and not care too much about annotating variables around.

Declaring a id __strong * argument may make sense in some cases, but in general it could lead to unexpected errors generated by the compiler.

My advice here is: "let the compiler do his magic and you'll be good"

Update

I don't see why compiler can not do the same trick for __strong argument.

Telling the compiler to handle in an __autoreleasing fashion the management of either a __strong or __weak variable it's ok since it basically means: "Please, compiler, do the right thing automatically".

That's why the trick seen above will work without issues.

On the other hand, if you declare a variable as __weak you presumably have a good reason for doing so and the last thing you want is to have it implicitly retained when you clearly specified otherwise. That would radically change the semantic of the piece of code you've written, therefore the compiler won't do that (thank God!).

In other words

__weak --> __autoreleasing good
__strong --> __autoreleasing good
__weak <--> __strong wrong!

like image 101
Gabriele Petronella Avatar answered Nov 10 '22 15:11

Gabriele Petronella


The only advantage is, as you said, that the object is autoreleased on return. So without ARC it's the same as sending retain and autorelease. In C every variable passed as argument is copied, so this doesn't influence what will be done with the original pointer, but just with the copied pointer.

An example of advantage could be this, let's say the argument isn't __autoreleasing:

-(BOOL)performOperationWithError:(NSError * __strong *)error;

So I call the method passing a weak reference:

NSError* __weak error;
[object performSelectorWithError: &error];

What happens here? The copied argument isn't autoreleased on return, so when the method returns error is nil. If instead the method was this one:

-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;

This case the error had still a retain count of 1, but it was autoreleased, so it wasn't nil and could have been used inside the pool.

like image 31
Ramy Al Zuhouri Avatar answered Nov 10 '22 14:11

Ramy Al Zuhouri


The other reason which I haven't seen mentioned is to follow Cocoa conventions so that ARC code can properly interoperate with non-ARC code.

like image 1
Ken Thomases Avatar answered Nov 10 '22 16:11

Ken Thomases