Upcoming and OnDemand Webinars View full list

Unsafe at Any Speed

Mark Dalrymple

__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.

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project