Unsafe at Any Speed
__unsafe_unretained. That sounds pretty scary. It’s a new symbol added by ARC that’s used to decorate pointers. It pops up in Xcode’s autocomplete occasionally, and sometimes you see it in code you find on the net. What is it? When would you want to use it?
The What
__unsafe_unretained is a “variable lifetime qualifier” which you put before an Objective-C object pointer. It tells the compiler how to manage the memory that the pointer is pointing to. Other qualifiers are __strong (for strong references), __weak (for zeroing weak references), and __autoreleasing (for objects returned by reference and should be treated as autoreleased).
__strong and __weak are certainly the most common qualifiers you’ll see. __strong says “retain the object when it assigned to this pointer, and release it when the pointer value changes”, and __weak says “don’t retain the object when assigned to this pointer, and when the object is deallocated, come back and scribble zeros into this pointer.” Under ARC, pointers are __strong by default, and are also zero-initialized, so they’re always safe, even when declared as a local variable.
__unsafe_unretained tells the compiler to do nothing with that pointer. Don’t retain objects on assignment. Don’t release them when the pointer changes. Don’t back-fill that pointer with zeros. Sounds pretty dangerous, doesn’t it? Sounds almost… unsafe.
It’s not any more dangerous than pre-ARC life, because that’s the way non-ARC Objective C works. Objects aren’t automatically retained or released (gotta do it yourself), and pointers don’t get zeroed if what they point to goes away. When you did something like
NSThing *thisThing = [[NSThing alloc] init]; NSThing *thatThing = thisThing;
The four bytes of the thisThing pointer just got copied into thatThing. (If you’re running Mac 64-bit, it’s eight bytes, but for sake of discussion I’m just going to refer to pointers as four bytes, vs the multitude of bytes which comprise the object.) No additional memory management was done. If you then did
[thisThing release];
thisThing's retain count would go to zero and the object deallocated. Unfortunately, thatThing is still pointing to the memory that thisThing previously occupied. This where a bug is born, and you now have some fun debugging opportunities in your future.
Under ARC, life is different: pointers are strong by default. Here’s a class that just prints something out when instances die. You can get the code for the complete example at this gist:
@interface NSThing : NSObject
@end
@implementation NSThing
- (void) dealloc {
NSLog (@"dealloc called for thing %p", self);
} // dealloc
@end // NSThing
So under ARC, the code:
NSThing *thisThing = [[NSThing alloc] init]; NSThing *thatThing = thisThing;
Causes thisThing and thatThing to point to the same object, and it has a retain count of two. The object will live as long as either of these pointers point to it. If you zero out thisThing:
thisThing = nil; NSLog (@"that thing is %@", thatThing);
thatThing is still pointing to it, and causes it to stay alive:
unsafe[24085:303] that thing is <NSThing: 0x7fe1f2900100> unsafe[24085:303] dealloc called for thing 0x7fe1f2900100
The dealloc occurs when the compiler realizes that thatThing is no longer used and can be removed, but you can see it happens after the use of thatThing in the log.
Now change thatThing to a weak reference:
NSThing *thisThing = [[NSThing alloc] init]; __weak NSThing *thatThing = thisThing;
So now, when thatThing is assigned, the address of the object is copied into the four bytes of thatThing, but no additional retains are made. If you then zero out the original reference, and print out the value of the weak pointer, you’ll see the object has gone away (the dealloc happens), and thatThing has been zeroed out automatically:
unsafe[24085:303] dealloc called for thing 0x7fe1f2900100 unsafe[24085:303] that thing is (null)
Now, finally, __unsafe_unretained. What will happen in that case? Decorate the pointer:
NSThing *thisThing = [[NSThing alloc] init]; __unsafe_unretained NSThing *thatThing = thisThing;
Zero out the original reference and print out the value of the thatThing pointer:
thisThing = nil; NSLog (@"that thing is %@", thatThing);
And you have the worst case scenario:
unsafe[24085:303] dealloc called for thing 0x7fe1f2900100 unsafe[24085:303] that thing is <NSThing: 0x7fe1f2900100>
A pointer pointing to a deallocated object.
The Why
Sounds dangerous. Why would you want to use that? Why would the LLVM team even add that in? Why not just make all object pointers safe – either strong or weak?
There are some classes that can’t have weak references assigned to them, such as NSImage and NSFont (and others). In OS X 10.7 you can’t have weak references to those, and other classes like NSWindow or NSWindowController (and others), due to legacy or technical details, such as a custom retain and release implementation (Thanks to Mike Ash for the hint as to why some of these classes are off-limits). But there are times you might want to point to them and not create a strong reference. Perhaps it’s to break a retain cycle. You’d use __unsafe_unretained for those.
Also, ARC has a restriction on object pointers that live in structs and unions – they’re not supported. ARC will not allow a strong or a weak reference to a pointer in a struct. If you try, the compiler will smack you down:
struct StructWithObject {
NSThing *otherThing;
};
Will give you the delightful error:
unsafe.m:50:18: error: ARC forbids Objective-C objects in structs or unions
NSThing *otherThing;
^
Why no object pointers in structs? Reliability. The compiler can reliably see every assignment to a strong or weak pointer, and can generate the retains and releases behind the scenes. The compiler can also reliably see every assignment to an object’s instance variables and can emit the retains and releases as well.
With structs, it’s not so easy. You can assign structs with the standard C assignment operator:
StructWithObject thing1 = ...; StructWithObject thing2; thing2 = thing1;
That last line of code basically does a memcpy of all the bytes (sizeof(StructWithObject)) of thing1 into the storage area of thing2. It’s very fast, very straightforward code. ARCifying the structs would require the emitting of function calls along with the struct copying, and now a simple operation has become more complicated. Not a show-stopper by any means, but it is extra work.
Non-ARC source files are the show-stopper. When those files are compiled, any struct assignments become like memcpy, with no reason for the compiler to emit retains or releases. Why isn’t this a problem with plain old object pointers? It’s our responsibility to manage memory management, and we have tools to help us get it right. Plain old structures have way too may opportunities to break the world behind ARC’s back.
Unions are also a real killer. It’s perfectly cromulent to have a union that has an object pointer share the same bytes as an int or a float. There’s no way for the compiler to know that a pointer value is not being assigned via the int or float part of the union. Garbage collectors don’t have any problem with this kind of thing because they scan through memory looking for things that look like addresses. But given the way ARC works (with the compiler adding retains and releases, and not scanning memory), that’s an unfixable case.
So what if you really really really want to point to an object in a structure? You would use __unsafe_unretained :
struct StructWithObject {
__unsafe_unretained NSThing *otherThing;
};
The compiler happily allows this. It’s your responsibility that the object is alive for the lifetime of its address being kept in this structure. If the object is deallocated, nothing happens to the structure, and the pointer is pointing to some bad place. If you then try to use the pointer, well, you now have some fun debugging opportunities in your future.
Lastly, you would use __unsafe_unretained in Mac OS X 10.6 or iOS 4 instead of __weak. ARC works on those older versions of the OS, with the caveat that zeroing weak references are not supported. Instead of using __weak (like you would use for delegates in iOS 5), you would have to use __unsafe_unretained for iOS 4. You could also use one of the libraries that implement weak references on those older platforms.
The End
So what’s the final word on __unsafe_unretained? For the most part, you just need to be aware it exists and what it means, in case you stumble across it. If you need Objective-C pointers inside of structs, you have to use it. If you’re targeting Mac OS X 10.6 or iOS 4, you’ll be using it instead of __weak. If you’re wanting non-strong references to the set of verboten objects, you’d use it as well. Otherwise, you’ll be sticking to __strong (or no decorations at all) and __weak for your regular day-to-day pointing.


8 Comments
It seems that __unsafe_unretained is sometimes used to get backward compatibility of the headers with non-ARC code.
__weak is not compatible.
Thanks! Fixed!
Found a typo: __unsafe_retained in 3rd paragraph in “The What” shold be __unsafe_UNretained
For what it’s worth, you /can/ put strong pointers to Objective-C objects in C++ objects. The ARC memory management will be implicitly added to the constructor and destructor. Note that this makes your C++ object non-POD, meaning it can’t be copied around with memcpy and such.
Yeah, that is *one* case where C++ integrates better with Objective-C than C does. Imagine that — Objective-C++ can be *worthwhile*, and not just “Objective-C and C++ smashed together in an unholy marriage” (bbum) that you reluctantly use when you want to call C++ code from an Objective-C class.
Unfortunately, it is sometimes necessary (though thankfully rare) to wrap Objective-C code in a C API, in which case you have no choice but to stuff an id or equivalent in a C struct and use __unsafe_unretained.
You should also mention that you can put Objective-C objects inside structs if you are using Objective-C++. Under ObjC++ it generates C++11 code to memory manage Objective-C objects for you. However you never see this code LLVM generates for you, it’s all done behind the scenes.
Sorry I’m late to this party, but I hit a snag and Google declares you the best hit.
Specifically, I need to include an Objective-C object pointer into a predefined struct whose type I cannot change. And ARC gives me the error about how I’m too dumb to safely manage that. (Boy, I wonder how I’ve coped these last 30 years – sorry, unnecessary sarcasm)
More background, my library is being passed an object of unknown provenance and in the non-ARC solution, it just retains it and sticks the pointer into the struct, which then gets passed out to non-objective-C code. When freeing the struct, I actually was capable of remembering to release the pointer, and there was never any chance of the struct being accidentally bit-copied.
Anyway, you suggest that __unsafe_retained gives you the opportunity to keep the pointer in the structure, but I become responsible for making sure the target object stays alive.
My question is *how* ? As far as I can tell, ARC takes the only tool I had available, -retain, away from me.
When ARC enters the picture, I can add the pointer to my struct but have no mechanism to increment the retain counter (sic) without being able to call something like retain. At the moment, my best bet is to add the object to a static NSCountedSet at retain time and remove it at release time. This seems clumsy beyond words but I can’t think of any other way.
Hey Jeff,
I hear ya
One of the examples in the third edition of AMOSXP returns a pair of objects from a method via a struct. Oh well.
The external collection would be the the first suggestion I’d make – toss it into a collection when using, and take it out when done. You could add a category on NSObject called “yoARCreallyRetain”. It’s actually not much different that what -retain is doing under the hood. NSObject doesn’t store the retain count, so it’s actually stored in an external structure (and really only stored if the retain count hits 2). So fundamentally there’s no difference.
You can also turn ARC off for just that file (or groups of files), by using -fno-objc-arc. Unlike the now-defunct GarbageCollection, you can freely mix ARC and non-ARC object files. You can #if __has_feature(objc_arc) in your source file and #error if someone tries to build it with ARC. There’s no reason everything has to be converted to ARC, and in this case it doesn’t make sense. A basket of working code is still a basket of *working* code, and if you don’t need to do full-scale restructuring, it’s generally not worth it.