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.
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.
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.
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.
}
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.
__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.
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"
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!
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.
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.
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