1

The following code is taken from "The iOS 5 Developer's Cookbook" used to illustrate how to write a string to a file. It makes use of __autoreleasing without any explanation. Why is it necessary?

NSError __autoreleasing error;
...
if (![myString writeToFile:path atomically:YES error:&error)
{
    NSLog(.... error.localizedFailureReason  ...);
    return;
}

Why not just declare the error on the stack without the use of __autoreleasing?

------ EDIT -----

Additional question: why is the author declaring NSError and not NSError*?

Gruntcakes
  • 37,738
  • 44
  • 184
  • 378

5 Answers5

7

You are missing what actually happens in this code when a variable is passed by reference and assigned in an ARC program. Take for example a function (BOOL)save:(NSError * __autoreleasing *)error

In non-ARC Programming, the save function looks like this:

- (BOOL)save:(NSError * __autoreleasing *)myError {
  *myError = [[[NSError error] retain] autorelease]
}

In ARC Programming, the save function looks like this:

- (BOOL)save:(NSError * __autoreleasing *)myError {
  *myError = [[NSError alloc] init];
}

Despite what the ARC code looks like, both save functions create an error object that has been retained and autoreleased.

This is because in the ARC version, the type of pointer that myError is determines what happens with the memory management of the error object. In effect, as long as the pointer is type __autoreleasing, the *myError assignment line is replaced with

*myError = [[[NSError error] retain] autorelease]

at runtime.

So if we were somehow able to pass in a pointer of the wrong type, for example __strong to the save function, it would cause the save function to do the wrong thing.

Since the compiler will prevent this from happening by creating a temporary variable, the code will work either way, but passing in a pointer of type other than __autoreleasing doesn't make sense from an ARC programming perspective.

Colin
  • 2,089
  • 25
  • 34
  • Great answer, one of the only one that actually explains what's going on beneath the hood. I have an additional question though. Why is it that the variables whose address to passed to `save` needs to be declared as `__autoreleasing`? The ARC transition guide says that otherwise a temporary variable pops up, but couldn't the compiler be smart about it? Or is there a semantic reason for the issue? – Norswap Nov 27 '13 at 10:36
  • 4
    @Norswap just imagine the save function is part of a precompiled library. When you return from the save function, ARC needs to know whether or not the error object (**myError) is already in an autorelease pool. And in this case it can't find out by looking at the source code of the save function, because it doesn't have it. It's about preserving the inputs to the ARC system. The only reason you don't need the same thing when you do (e.g.) myDict=[NSDictionary dictionary]), is because there is a strict convention that an object returned in this way is always autoreleased. – Colin Dec 02 '13 at 18:20
  • I guess the above comment is more of a direct answer to the OP's question than the above answer. What was interesting to me at the time of the answer was the fact that ARC is actually doing the same things behind the scenes that programmers did explicitly before ARC. It's part of a smart compiler, not part of a new runtime engine. – Colin Dec 02 '13 at 18:26
  • Thanks, that explains it nicely. – Norswap Dec 03 '13 at 08:45
3

It's a hint for the automatic reference counting (ARC) system.

The error object will be allocated somewhere in NSString's code so declaring it as __autoreleasing in your code lets ARC know what the storage characteristics are. That is, when error is set, it will be an autoreleased object.

gregheo
  • 4,182
  • 2
  • 23
  • 22
  • Most of the time NSError is declared as NSError* error. But in this case its NSError error. i.e. an NSError is being created before wrieToFile: Does this have consequences? – Gruntcakes Feb 29 '12 at 16:28
  • I actually submitted an edit to your original question with `NSError *error`. I think that must be a typo in the book: Obj-C objects are always referenced with pointers so `NSError error` will give a compile-time error. – gregheo Feb 29 '12 at 17:05
  • 1
    I am afraid this answer is incorrect. It is not a "hint" but an declaration of the semantics - *hints* can be ignored, *semantics* cannot be. Also the reference stored need *not* be to an autoreleased object. See [this question](http://stackoverflow.com/questions/8814718/handling-pointer-to-pointer-ownership-issues-in-arc) for more of the background. – CRD Apr 22 '14 at 18:54
2

From the ARC release notes at https://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html:

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

There's an implicit declaration of __strong made when you declare the variable, but since it is being passed by reference the compiler needs the hint in order to do the right thing. Whether or not it's a stack variable doesn't affect the retain/release tracking.

steve_sch
  • 624
  • 5
  • 4
1

the code :

__autoreleasing  NSString *str = xx;

will be compiled by ARC as:

NSString *str = xx.autorelease
Jian
  • 101
  • 1
  • 3
  • dot syntax without a property is not best practice. Besides it doesn't really answer why to use [xx autorelease] in ARC – Binarian Aug 11 '13 at 19:51
0

Well, it's an ARC convention to use __autoreleasing qualifier for object passed by reference. The explanation is here from clang site:

4.3.2. Storage duration of __autoreleasing objects

A program is ill-formed if it declares an __autoreleasing object of non-automatic storage duration.

Rationale: autorelease pools are tied to the current thread and scope by their nature. While it is possible to have temporary objects whose instance variables are filled with autoreleased objects, there is no way that ARC can provide any sort of safety guarantee there.

It is undefined behavior if a non-null pointer is assigned to an __autoreleasing object while an autorelease pool is in scope and then that object is read after the autorelease pool's scope is left.

Andrea
  • 26,120
  • 10
  • 85
  • 131