In which situations do we need to write the __autoreleasing ownership qualifier under ARC?
Asked Answered
B

4

127

I'm trying to complete the puzzle.

__strong is the default for all Objective-C retainable object pointers like NSObject, NSString, etc.. It's a strong reference. ARC balances it with a -release at the end of the scope.

__unsafe_unretained equals the old way. It's used for a weak pointer without retaining the retainable object.

__weak is like __unsafe_unretained except that it's an auto-zeroing weak reference meaning that the pointer will be set to nil as soon as the referenced object is deallocated. This eliminates the danger of dangling pointers and EXC_BAD_ACCESS errors.

But what exactly is __autoreleasing good for? I'm having a hard time finding practical examples on when I need to use this qualifier. I believe it's only for functions and methods which expect a pointer-pointer such as:

- (BOOL)save:(NSError**);

or

NSError *error = nil;
[database save:&error];

which under ARC has to be declared this way:

- (BOOL)save:(NSError* __autoreleasing *);

But this is too vague and I'd like to fully understand why. The code snippets I find place the __autoreleasing inbetween the two stars, which looks weird to me. The type is NSError** (a pointer-pointer to NSError), so why place __autoreleasing inbetween the stars and not simply in front of NSError**?

Also, there might be other situations in which I must rely on __autoreleasing.

Burstone answered 14/1, 2012 at 11:52 Comment(1)
I have this same question and the answers below aren't totally convincing... for example, why aren't the system provided interfaces that take NSError** arguments declared with the __autoreleasing decorator like you and the Transitioning to Arc Release Notes say they should be? e.g., Any of the many of these routines in NSFileManager.h ??Gaddis
Y
70

You're right. As the official documentation explains:

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

All of this is very well explained in the ARC transition guide.

In your NSError example, the declaration means __strong, implicitly:

NSError * e = nil;

Will be transformed to:

NSError * __strong error = nil;

When you call your save method:

- ( BOOL )save: ( NSError * __autoreleasing * );

The compiler will then have to create a temporary variable, set at __autoreleasing. So:

NSError * error = nil;
[ database save: &error ];

Will be transformed to:

NSError * __strong error = nil;
NSError * __autoreleasing tmpError = error;
[ database save: &tmpError ];
error = tmpError;

You may avoid this by declaring the error object as __autoreleasing, directly.

Yezd answered 14/1, 2012 at 11:58 Comment(11)
So ARC must autorelease the object it received by pass-by-writeback because when it creates it with a +1 retain count in the -save: method for example, it has to release the object after it has passed back via writeback - thus it has to autorelease it just like a convenience constructor which returns an autoreleased object. Right?Burstone
Yep, you can see it like that. As a convenience constructor, the creation of the object is done in the method. The difference is in such a case, the object is get ("returned") from a reference argument (NSError **). So ARC needs to know how to handle it.Yezd
Makes sense. Are there any other situations in which to use __autorelease? Afaik we don't have to do it when writing a method that returns a new object, because ARC takes care of it automatically. Why not in this case?Burstone
No, __autoreleasing is only used for arguments passed by reference. This is a special case, as you've got a pointer to an object's pointer. That's not the case with stuff like convenience constructors, as they only returns a pointer to an object, and as ARC handles it automatically.Yezd
Why is the __autoreleasing qualifier placed inbetween the stars, and not just in front of NSError**? This looks weird to me as the type is NSError**. Or is it because this is trying to indicate that the pointed-to NSError* pointer has to be qualified as pointing to an autoreleased object?Burstone
Actually what convenient constructor return should really be __autoreleasing I think. However, compiler sort of caught that and turn strong into autoreleasing.Linin
Still confused. NSURLConnection sendSynchronousRequest... has an output error and the header just declares it as such: "error:(NSError **)error". It appears the compiler is auto-injecting __autoreleasing when using output pointers?Sulphurbottom
@Proud Member in regards to your first comment - that is incorrect (if I understand you correctly) - see Glen Low's answer below. The error object is created and assigned to an autoreleasing variable (the one you passed in) inside the save function. This assignment causes the object to be retained and autoreleased at that time. The declaration of the save function prevents us from sending it anything other than an autoreleasing variable because that is what it needs - which is why the compiler creates a temporary variable if we try.Newby
@ProudMember see my answer below (exceeded character count for a comment). https://mcmap.net/q/128963/-in-which-situations-do-we-need-to-write-the-__autoreleasing-ownership-qualifier-under-arcMortie
So why do none of the Apple interfaces seem to have this? e.g., everything in NSFileManager.h?Gaddis
@Macmade: Just by chance I noticed that your answer has been edited (by stackoverflow.com/users/12652/comptrol) and I have the impression that at least the changes to your first example ("implicitly ... will be transformed to ...) are wrong, because the __strong qualifier has been moved from the second line to the first line. Perhaps you could check that.Carmellacarmelle
M
38

Following up on Macmade's answer and Proud Member's follow up question in the comments, (would have also posted this as a comment but it exceeds the max character count):

Here is why the variable qualifier of __autoreleasing is placed between the two stars.

To preface, the correct syntax for declaring an object pointer with a qualifier is:

NSError * __qualifier someError;

The compiler will forgive this:

__qualifier NSError *someError;

but it isn't correct. See the Apple ARC transition guide (read the section that begins "You should decorate variables correctly...").

To address to the question at hand: A double pointer cannot have an ARC memory management qualifier because a pointer that points to a memory address is a pointer to a primitive type, not a pointer to an object. However, when you declare a double pointer, ARC does want to know what the memory management rules are for the second pointer. That's why double pointer variables are specified as:

SomeClass * __qualifier *someVariable;

So in the case of a method argument that is a double NSError pointer, the data type is declared as:

- (BOOL)save:(NSError* __autoreleasing *)errorPointer;

which in English says "pointer to an __autoreleasing NSError object pointer".

Mortie answered 31/10, 2012 at 21:55 Comment(1)
Thanks. THAT was the answer I need, and you could set "because a pointer that points to a memory address is a pointer to a primitive type, not a pointer to an object." in bold, which is the heart of the thing.Nocturnal
I
17

The definitive ARC specification says that

For __autoreleasing objects, the new pointer is retained, autoreleased, and stored into the lvalue using primitive semantics.

So for example, the code

NSError* __autoreleasing error = someError;

actually gets converted to

NSError* error = [[someError retain] autorelease];

... which is why it works when you have a parameter NSError* __autoreleasing * errorPointer, the called method will then assign the error to *errorPointer and the above semantics will kick in.

You could use __autoreleasing in a different context to force an ARC object into the autorelease pool, but that's not terribly useful since ARC only seems to use the autorelease pool at method return and already handles that automatically.

Incantation answered 25/7, 2012 at 0:6 Comment(0)
P
0

To be short: this is only for compatibility with MRC.

Apple have made agreement that in own libraries objects returned by ** are always autoreleased. So ARC code will work fine with old binaries (for example if you have deployment target iOS 4) and vise versa MRC code will work fine with ARC binaries.

So in conclusion:

  • You should never use __autoreleasing: compiler will automatically add it where needed

  • If you are not going to support MRC code, then you should use * __strong * everywhere. It will save from crashes of family:

    @autoreleasingpool {
       *autorelesingOut = [@"crash maker" mutableCopy];//NSString * __autoreleasing *autorelesingOut;
       *strongOut = [@"it's ok" mutableCopy];//NSString * __strong *strongOut;
       //App will crash if autorelesingOut will be referenced outside of this autoreleasepool
    }
    
Pulchia answered 3/12, 2021 at 11:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.