Automatic Reference Counting

Friday Q&A 2011-09-30: Automatic Reference Counting

Since the moment Apple announced it, readers have asked me to write about Automatic Reference Counting, or ARC. Today is the day. I'll talk about Apple's new memory management system, how it works, and how to get the most out of it.

Conceptual
The Clang static analyzer is a really useful tool for finding memory management errors in code. If you're like me, you've looked at the output of the analyzer and thought, "If you can spot the error, why can't you just fix it for me too?"

That, in essence, is what ARC is. The memory management rules are baked into the compiler, but instead of using them to help the programmer find mistakes, it simply inserts the necessary calls on its own.

ARC occupies a middle ground between garbage collection and manual memory management. Like garbage collection, ARC frees the programmer from writing retain/release/autorelease calls. Unlike garbage collection, however, ARC does not deal with retain cycles. Two objects with strong references to each other will never be collected under ARC, even if nothing else refers to them. Because of this, while ARC frees the programmer from dealing with most memory management issues, the programmer still has to avoid or manually break cycles of strong references in the object graph.

When it comes to implementation specifics, there's another key difference between ARC and Apple's implementation of garbage collection: ARC is not an either/or proposition. With Apple's garbage collector, either the entire application runs under GC, or none of it does. This means that all Objective-C code in an application, including all of Apple's frameworks and all third-party libraries you might include, must be GC compatible for you to take advantage of GC. In contrast, ARC coexists peacefully with non-ARC manual memory managed code in the same application. This makes it possible to convert projects piecemeal without the massive compatibility and reliability problems that garbage collection ran into when it was first introduced.

Xcode
ARC is available in Xcode 4.2, currently in beta, and only when compiling with Clang (a.k.a. "Apple LLVM compiler"). The setting is called, obviously enough, "Objective-C Automatic Reference Counting". Turn it on, and off you go.

If you're working on existing code, changing this setting will produce an enormous quantity of errors. ARC not only manages memory for you, but it forbids you from trying to do it yourself. It's illegal to manually send retain/release/autorelease when using ARC. Since normal non-ARC Cocoa code is littered with this stuff, you'll get a lot of errors.

Fortunately, Xcode offers a tool to convert existing code. Select Edit -> Refactor... -> Convert to Objective-C ARC... and Xcode will guide you through converting your code. Although there may be some situations where it needs help figuring out what to do, the process should be largely automatic.

Basic Functionality
Cocoa memory management rules are fairly simple. In short:

  1. If you allocnewcopy, or retain an object, you must balance that with release or autorelease.
  2. If you obtain an object from something other than the above, and you need it to stay alive long-term, you must retain or copy it. This must, of course, be balanced later.

These are highly suitable for automation. If you write this:

    Foo *foo = [[Foo alloc] init];
    [foo something];
    return;

The compiler can see the unbalanced alloc. The code is thus transformed:

    Foo *foo = [[Foo alloc] init];
    [foo something];
    [foo release];
    return;

In reality, the compiler does not insert a message send to release. Instead, it inserts a call to a special runtime function:

    Foo *foo = [[Foo alloc] init];
    [foo something];
    objc_release(foo);
    return;

This allows for some optimization. In the common case where -release is not overridden, the objc_release function can bypass Objective-C messaging, resulting in a bit of a speed gain.

This automation can make code safer. Most Cocoa programmers interpret "long-term" in rule #2 to mean objects stored in instance variables and similar places. We don't normally retain and release local temporary objects:

    Foo *foo = [self foo];
    [foo bar];
    [foo baz];
    [foo quux];

However, this can be dangerous:

    Foo *foo = [self foo];
    [foo bar];
    [foo baz];
    [self setFoo: newFoo];
    [foo quux]; // crash

The standard way to work around this is to have the -foo getter do a retain/autorelease before returning the value. This works, but can build up a lot of temporary objects leading to excessive memory usage. ARC, however, will be paranoid and insert extra calls here:

    Foo *foo = objc_retainAutoreleasedReturnValue([self foo]);
    [foo bar];
    [foo baz];
    [self setFoo: newFoo];
    [foo quux]; // fine
    objc_release(foo);

Likewise, even if you write a plain getter, ARC will make it safe as well:

    - (Foo *)foo
    {
        return objc_retainAutoreleaseReturnValue(_foo);
    }

But wait, this doesn't solve the problem of excessive temporary objects at all! We're still doing a retain/autorelease sequence in the getter, and a retain/releasecombination in the calling code. This is considerably less efficient!

Not to worry. As I mentioned above, ARC emits these special calls instead of plain message sends for the purposes of optimization. In addition to simply making retain andrelease faster, these calls are able to eliminate certain operations altogether.

When objc_retainAutoreleaseReturnValue runs, it looks on the stack and grabs the return address from its caller. This allows it to see exactly what will happen after it finishes. When compiler optimizations are turned on, the call to objc_retainAutoreleaseReturnValue will be subject to tail-call optimization, and the return address will point to the call to objc_retainAutoreleasedReturnValue.

With this crazy return-address examination, the runtime is able to see that it's about to perform some redundant work. It therefore eliminates the autorelease, and sets a flag that tells the caller to eliminate its retain. The whole sequence ends up doing a single retain in the getter and a single release in the calling code, which is both completely safe and efficient.

Note that this optimization is fully compatible with non-ARC code. In the event that the getter doesn't use ARC, the flag won't be set and the caller will perform a fullretain/release combination. In the event that the getter uses ARC but the caller does not, the getter will see that it's not returning to code that immediately calls the special runtime function, and will perform a full retain/autorelease combination. Some efficiency is lost, but correctness is preserved.

In addition to all of this, ARC also automatically creates or fills out a -dealloc method for all classes to release their instance variables. It's still possible to manually implement-dealloc, and it's necessary for classes which manage external resources, but it's no longer necessary (or possible) to manually release instance variables. ARC will even put the [super dealloc] at the end for you, so you don't have to. Previously, you might have written this:

    - (void)dealloc
    {
        [ivar1 release];
        [ivar2 release];
        free(buffer);

        [super dealloc];
    }

Now you can just write this:

    - (void)dealloc
    {
        free(buffer);
    }

In the event that your -dealloc method just releases instance variables, it can simply be eliminated altogether.

Cycles and Weak References
ARC still requires the programmer to manually resolve reference cycles, and the best way to resolve reference cycles is typically to use weak references.

ARC provides zeroing weak references. These are weak references which not only don't keep the referenced object alive, but which also automatically become nil when the referenced object is destroyed. Zeroing weak references avoid the potential for dangling pointers and the associated crashes and mysterious behavior.

To make a zeroing weak variable, simply prefix its declaration with __weak. For example, here is a weak instance variable:

    @interface Foo : NSObject
    {
        __weak Bar *_weakBar;
    }

Likewise for local variables:

    __weak Foo *_weakFoo = [object foo];

You can then use it like any other variable, with the value automatically becoming nil when appropriate:

    [_weakBar doSomethingIfStillAlive];

Note, however, that a __weak variable can become nil at almost any time. Memory management is an inherently multithreaded activity, and a weakly referenced object could be destroyed on one thread while another thread is accessing it. Code like this is therefore not valid:

    if(_weakBar)
        [self mustNotBeNil: _weakBar];

Instead, store the object into a local strong reference and test that:

    Bar *bar = _weakBar;
    if(bar)
        [self mustNotBeNil: bar];

Because bar is a strong reference here, the object is guaranteed to stay alive (and the variable non-nil) throughout this code.

ARC's implementation of zeroing weak references requires close coordination between the Objective-C reference counting system and the zeroing weak reference system. This means that any class which overrides retain and release can't be the target of a zeroing weak reference. While this is uncommon, some Cocoa classes, like NSWindow, suffer from this limitation. Fortunately, if you hit one of these cases, you will know it immediately, as your program will crash with a message like this:

    objc[2478]: cannot form weak reference to instance (0x10360f000) of class NSWindow

If you really must make a weak reference to classes such as these, you can use the __unsafe_unretained qualifier in place of __weak. This creates a weak reference which is not zeroing. You must ensure that you never use such a pointer (preferably by zeroing it out manually) after the object it points to has been destroyed. Be careful, as non-zeroing weak references are playing with fire.

While it's possible to build programs using ARC that run on Mac OS X 10.6 and iOS 4, zeroing weak references are not available on those OSes. All weak references must be__unsafe_unretained here. Because non-zeroing weak references are so dangerous, this limitation significantly decreases the attractiveness of ARC on those OSes in my view.

Properties
Since properties are so tightly coupled to memory management, it makes sense that ARC would introduce some new behaviors there.

ARC introduces a few new ownership modifiers. Declaring a property as strong makes that property a strong reference. Declaring it as weak uses a zeroing weak reference. The unsafe_unretained modifier uses a non-zeroing weak reference. When @synthesize is used, the compiler creates an instance variable of the same storage type.

The existing modifiers of assigncopy, and retain still exist and work the same way they did before. Notably, assign creates a non-zeroing weak reference, so it should be avoided whenever possible.

Aside from the new modifiers, properties work just as they always have.

Blocks
Blocks are Objective-C objects, and as such are also managed by ARC. Blocks have special memory management requirements, and ARC treats them accordingly. Block literals must be copied, not retained, which in practice means that it's best to copy rather than retain blocks everywhere. ARC follows this practice.

Additionally, ARC knows that block literals must be copied if they're used after the current scope returns. Non-ARC code needs to explicitly copy and autorelease returned blocks:

    return [[^{
        DoSomethingMagical();
    } copy] autorelease];

With ARC, this simply becomes:

    return ^{ DoSomethingMagical(); };

However, beware! ARC currently does not automatically copy a block literal that's converted to an id. So while this code is fine:

    dispatch_block_t function(void)
    {
        return ^{ DoSomethingMagical(); };
    }

This code is not:

    id function(void)
    {
        return ^{ DoSomethingMagical(); };
    }

It's easy to work around by simply copying the block, but it's something to be careful with:

    return [^{ DoSomethingMagical(); } copy];

Likewise, you need to explicitly copy blocks that you pass as id parameters:

    [myArray addObject: [^{ DoSomethingMagical(); } copy]];

Fortunately, it appears that this is just an edge case that fell through the cracks and is likely to be fixed soon. There's no problem with throwing in an extra manual copy if you're unsure.

Another significant change with ARC is the behavior of __block qualified variables. The __block qualifier allows a block to modify a captured variable:

    id x;
    __block id y;
    void (^block)(void) = ^{
        x = [NSString string]; // error
        y = [NSString string]; // works
    };

Without ARC, __block also has the side effect of not retaining its contents when it's captured by a block. Blocks will automatically retain and release any object pointers they capture, but __block pointers are special-cased and act as a weak pointer. It's become a common pattern to rely on this behavior by using __block to avoid retain cycles.

Under ARC, __block now retains its contents just like other captured object pointers. Code that uses __block to avoid retain cycles won't work anymore. Instead, use__weak as described above.

Toll-Free Bridging
ARC only works on Objective-C types. CoreFoundation types still have to be managed manually by the programmer. Because there is ambiguity about ownership, ARC forbids standard casts between pointers to Objective-C objects and pointers of other types, including pointers to CoreFoundation objects. The following code, which is fairly typical under manual memory management, does not compile with ARC:

    id obj = (id)CFDictionaryGetValue(cfDict, key);

In order to make this compile again, you must tell ARC about the ownership semantics involved by using special casting annotations. These annotations are __bridge,__bridge_retained, and __bridge_transfer.

The simplest one to understand is __bridge. This is a direct conversion with no ownership consequences. ARC receives the value and then manages it normally. This is what we want for the above:

    id obj = (__bridge id)CFDictionaryGetValue(cfDict, key);

The other casting annotations transfer ownership to and from the ARC system. These can help ease the pain when bridging back and forth.

Here's an example of using bridging in a situation where the returned object needs to be released.

    NSString *value = (NSString *)CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
    [self useValue: value];
    [value release];

If we move this to ARC by using __bridge, removing the release, and otherwise not making any changes, we end up with a leak:

    NSString *value = (__bridge NSString *)CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
    [self useValue: value];

The Copy in this code needs to be balanced with a release. ARC will emit a retain when initializing value, then balance that with a release when value is no longer used. Since nothing balances the original Copy, that object is leaked.

We can work around this with a little extra code:

    CFStringRef valueCF = CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
    NSString *value = (__bridge NSString *)valueCF;
    CFRelease(valueCF);

    [self useValue: value];

This is getting fairly verbose, though. Since the whole point of toll-free bridging is to be as painless as possible, and the whole point of ARC is to remove the need to write memory management code, it would be nice if this could be made more straightforward.

The __bridge_transfer annotation solves this problem. Rather than simply move a pointer value into ARC, it moves the value and transfers ownership. When__bridge_transfer is used in a cast, it tells ARC that this object is already retained, and that ARC doesn't need to retain it again. Since ARC takes ownership, it will still release it when it's done. The net result is that everything works the way it's supposed to:

    NSString *value = (__bridge_transfer NSString *)CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
    [self useValue: value];

Toll-free bridging works both ways. As before, ARC doesn't allow a standard cast to convert from an Objective-C object pointer to a CoreFoundation object pointer. This code won't compile under ARC:

    CFStringRef value = (CFStringRef)[self someString];
    UseCFStringValue(value);

Adding a __bridge to the cast makes it compile, but the resulting code is dangerous:

    CFStringRef value = (__bridge CFStringRef)[self someString];
    UseCFStringValue(value);

Since ARC doesn't manage the lifetime of value, it will release ownership of the object immediately, before it gets passed to UseCFStringValue, potentially causing a crash or other misbehavior. By using __bridge_retained, we can tell ARC to transfer ownership out of the system and into our hands. Since ownership is transferred, we're now responsible for releasing the object when done with it, just like with any other CF code:

    CFStringRef value = (__bridge_retained CFStringRef)[self someString];
    UseCFStringValue(value);
    CFRelease(value);

These cast annotations are useful outside of toll-free bridging as well. Any time you need to store an object pointer in storage that's not managed as an Objective-C object, they smooth the way. There are void * context pointers found in various places in Cocoa, and a prominent example is sheets. Without ARC:

    NSDictionary *contextDict = [NSDictionary dictionary...];
    [NSApp beginSheet: sheetWindow
       modalForWindow: mainWindow
        modalDelegate: self
       didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
          contextInfo: [contextDict retain]];


    - (void)sheetDidEnd: (NSWindow *)sheet returnCode: (NSInteger)code contextInfo: (void *)contextInfo
    {
        NSDictionary *contextDict = [(id)contextInfo autorelease];
        if(code == NSRunStoppedResponse)
            ...
    }

As before, this fails under ARC, because normal casts between object and non-object pointers are not allowed. However, using the cast modifiers, we can not only get ARC to allow it, but also get ARC to do the requisite memory management for us:

    NSDictionary *contextDict = [NSDictionary dictionary...];
    [NSApp beginSheet: sheetWindow
       modalForWindow: mainWindow
        modalDelegate: self
       didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
          contextInfo: (__bridge_retained void *)contextDict];


    - (void)sheetDidEnd: (NSWindow *)sheet returnCode: (NSInteger)code contextInfo: (void *)contextInfo
    {
        NSDictionary *contextDict = (__bridge_transfer NSDictionary *)contextInfo;
        if(code == NSRunStoppedResponse)
            ...
    }

To summarize:

  • __bridge simply transfers a pointer between ARC and non-ARC with no transfer of ownership.
  • __bridge_transfer moves a non-Objective-C pointer to Objective-C and also transfers ownership, such that ARC will release the value for you.
  • __bridge_retained moves an Objective-C pointer to a non-Objective-C pointer and also transfers ownership, such that you, the programmer, are responsible for later calling CFRelease or otherwise releasing ownership of the object.

Structs
Under ARC, structs and Objective-C object pointers pretty much don't mix. The problem is that there is no good way for the compiler to know when a particular struct is destroyed or copied, and thus no good place for the compiler to insert the necessary retain and release calls. Because it's such a difficult problem, and because putting object pointers in structs is so unusual anyway, ARC just gives up on the whole proposition. If you want to put an Objective-C object pointer in a struct, you must qualify it with__unsafe_unretained, and deal with all of the problems and danger that this implies.

Because it's so uncommon to put Objective-C pointers into structs, it's likely that this won't be a problem for your code. If it is, your best bet is to change the struct into a lightweight Objective-C class instead. ARC will start managing memory for you and the problem goes away.

Further Reading
While Apple's official documentation on ARC is still under wraps while Xcode 4.2 is in beta, a great deal of information about the system is available on the Clang site here:http://clang.llvm.org/docs/AutomaticReferenceCounting.html

Conclusion
Automatic reference counting substantially reduces the burden on the programmer for dealing with memory management. ARC is not a full garbage collector. It cannot detect retain cycles, and these must be dealt with and broken by the programmer. It still takes a lot of the grunt work out of writing Cocoa code, and the zeroing weak references that it provides are a powerful tool for dealing with cycles.

Things get trickier when it comes to CoreFoundation objects and toll-free bridging. ARC limits itself to dealing with Objective-C, so the programmer still needs to manage the CoreFoundation side manually. When converting between Objective-C and CoreFoundation pointers, the special __bridge cast modifiers need to be used to inform ARC about the memory management semantics of the conversion.

That wraps up today's exploration of Apple's latest programming language technology. Come back in two weeks for another fine word salad. Until then, keep watching the skies and sending in your suggestions for topics.

Did you enjoy this article? I'm selling a whole book full of them. It's available for iBooks and Kindle, plus a direct download in PDF and ePub format. It's also available in paper for the old-fashioned.  Click here for more information.

Comments:

Preston  at 2011-09-30 15:57:48:
Other than the automatic breaking of retain cycles, do you believe that any practical reason exists to use garbage collection instead of ARC? Up until ARC's announcement, GC was still being evangelized by some Apple developers on the Cocoa mailing list and sites like StackOverflow, making ARC's announcement slightly surprising.

Marc  at 2011-09-30 16:18:44:
What about the previous retain/copy properties, how would one specify that you want a copy setter/getter synthesized with a strong property? 

Also, how would you override weak/strong synthesized properties with ARC?

Iain  at 2011-09-30 16:30:11:
What's the correct way to declare simple (number) properties with ARC? 
I used to write @property(assign) BOOL flag; but now it sounds like assign should be avoided. But "unsafe-unretained' doesn't sound like it would be problematic with, say, an NSInteger ivar. Do I understand this correctly?

mikeash  at 2011-09-30 16:36:56:
Preston: The only thing that separates ARC from being a true garbage collector is being able to handle retain cycles. Automatic refcounting plus a cycle collector is a pretty common way to do garbage collection, actually. IIRC both Python and PHP work this way, for example. However, I think that the benefit of automatic cycle management shouldn't be understated. Cycles can happen in places where you wouldn't expect, so there's definitely value in having them broken for you. 

However, the language around ARC makes it sound like Apple is planning to phase out GC in favor of ARC, so even though GC has advantages, these are probably outweighed by the fact that ARC code will have better longevity and portability. 

Marc: Copy properties are fine, and are implicitly strong. Retain properties are also strong, but it's preferred that you explicitly use  strong instead. For overriding, all you need to do is make sure that the backing storage has the correct semantics. If you write your own accessors for a strong property, a normal ivar will do. For a weak property, a __weak ivar will do it. 

Iain: ARC only affects object pointers, so for primitives you can simply carry on as usual.

Dave  at 2011-09-30 16:47:24:
You should probably be using these functions instead of the casting modifiers: 

CFBridgingRetain() 
CFBridgingRelease()

Brian  at 2011-09-30 18:18:25:
Thanks Mike. This is much clearer than the Apple docs. The examples are very helpful.

ken  at 2011-09-30 18:27:58:
Nice article! 

In the places where you meant  objc_autoreleaseReturnValue you wrote  objc_retainAutoreleaseReturnValue

Also, Dave++. If you use CFBridgingRetain and CFBridgingRelease, then the CF ref count looks balanced. For example, 

NSString *str = CFBridgingRelease(CFStringCreate(...)); 

I find the semantics much easier to keep track of, and it's efficient. CFBridgingRelease is basically transfer one ref count from CF to objc, and the stack's strong, so in effect that is "don't generate any code, but make stuff look balanced".

mikeash  at 2011-09-30 18:57:19:
Dave: Alas, those are not publicly documented yet. I've actually found the casting modifiers growing on me while working on this stuff. 

ken: I'm pretty sure I want objc_retainAutoreleaseReturnValue, since an ivar accessor needs to do a retain/autorelease combo. 

Regarding the efficiency of the bridging functions, I thought they were just doing the same thing as the casts, so they should be the same, efficiency-wise.

David  at 2011-09-30 19:10:46:
If you are exposing a Block property, you should always declare the property as copy, not strong. The current compiler has a bug where a Block property that is declared strong is retained instead of copied, which can lead to crashing issues.

charles  at 2011-09-30 19:25:47:
Very nice write-up, thanks very much, Mike! 

So, what's your take on ARC: a good thing, a meh thing, 'good riddance GC', none of the above?... :-)

mikeash  at 2011-09-30 19:30:34:
My take on ARC has two facets. 

Taken entirely on its own, ARC is great. It gets rid of a ton of grunt work at a minimum of overhead and complexity. It's a perfect case where the compiler can step in and take a bunch of boring stuff away from the programmer. 

Taken in the larger context, where all of this work on ARC fairly heavily implies that Apple has given up on garbage collection, I'm sad. The ObjC GC has a lot of problems, but I was hopeful that it could be made better with time.

Mike Gerasimenko  at 2011-09-30 19:47:54:
If you would use the  "Leaks" tool with ARC it will show you the cycle references. 

Also, in the nice graphical way: 
http://dl.dropbox.com/u/27714647/screen.png 

You can read about it (and other things about ARC) in the presentation here 
http://dl.dropbox.com/u/27714647/ITJam2011%20ARC.key

ken  at 2011-09-30 20:22:33:
This one is correct: 

    Foo *foo = objc_retainAutoreleasedReturnValue([self foo]); 


this one should be  objc_autoreleaseReturnValue


    - (Foo *)foo 
    { 
        return objc_retainAutoreleaseReturnValue(_foo); 
    } 


They have to be paired. 

mikeash  at 2011-09-30 21:34:53:
According to the Clang document I linked above,  objc_autoreleaseReturnValue just autoreleases the value passed to it, as you would expect. This is incorrect to do to an ivar returned from a basic accessor, and will lead to an over-release and likely subsequent crash. 

objc_retainAutoreleaseReturnValue does a retain and an autorelease, which is precisely what is needed here. 

objc_retainAutoreleasedReturnValue can be paired with either of the above. Which one of the above is needed depends entirely on the semantics of the called method. If the called method allocs (or retains or copies) something and then needs to return it to the caller, it will use  objc_autoreleaseReturnValue. If the called method is returning a value which it has  not alloced (or retained or copied), like an ivar, then it will use  objc_retainAutoreleaseReturnValue to do the retain/autorelease combination necessary for that case.

mikeash  at 2011-09-30 21:39:57:
What I said can easily be verified by simply compiling some code and looking at the disassembly: 

-[TestClass foo]: 
0000000100000dd0    pushq    %rbp 
0000000100000dd1    movq    %rsp,%rbp 
0000000100000dd4    movq    0x0000040d(%rip),%rax 
0000000100000ddb    movq    (%rdi,%rax),%rdi 
0000000100000ddf    popq    %rbp 
0000000100000de0    jmp    0x100000e7a    ; symbol stub for: _objc_retainAutoreleaseReturnValue 
0000000100000de5    nopl    %cs:0x00000000(%rax,%rax)
 

nevyn  at 2011-09-30 21:44:46:
Yay, I finally understand __bridge*! Thanks! Good block memory management summary too, I lazilly linked to it instead of writing about it myself from my blocks guide... 

I'm rather sad that the GC seems to be dying, I wonder if that means macruby's future suddenly turned dark :( Would so love to use that on iOS...

Joshua Emmons  at 2011-09-30 21:55:24:
Thanks so much for this explanation of toll-free bridging with ARC! I've been combing message boards, documentation, and headers for any sort of clue as to how this is supposed to work, and you elegant examples are the first to make sense to me.

Grzegorz Adam Hankiewicz  at 2011-09-30 22:33:09:
How will ARC's weak references affect your MAZeroingWeakRef project? Will code using MAZeroingWeakRef convert to ARC without problems or should it be stripped in favor of the new __weak prefix?

mikeash  at 2011-10-01 00:20:37:
nevyn: I wonder how practical it would be for third parties to add a cycle detector on top of ARC to turn it into a real GC. Performance would probably not be the best, but it may not be completely ridiculous.... 

Grzegorz Adam Hankiewicz: MAZeroingWeakRef can be used fine in ARC projects as long as those particular files are compiled as non-ARC. MAZWR is useful if you're using ARC but targeting 10.6/iOS4 where official ZWRs aren't available. If you're targeting 10.7/iOS5, then MAZWR doesn't offer any real advantage over the built-in stuff, and in fact it will use the built-in stuff when available. 

(The one potential advantage of MAZWR is that it can work with classes like NSWindow which don't work with the ARC version. Right now it doesn't, but in theory it could fall back to the dynamic subclass craziness for those classes. I'm not sure if this is worthwhile to build.)

Scott Little  at 2011-10-01 09:53:00:
Nice article, very well explained. 

I think I have some unlearning to do though, since this sentence: 

There's no problem with throwing in an extra manual copy if you're unsure.

made my spine crawl just a little bit :-p 

scott

mikeash  at 2011-10-01 12:16:50:
Scott Little: Understandable. However, consider: once a block has been copied to the heap, further copies do nothing but increment the reference count, so they are essentially free. ARC will automatically balance those not-copies with releases. So, while it sounds bad, there really is no harm in doing a  [block copy] where it's not necessary, when using ARC.

mkeiser  at 2011-10-02 20:16:35:
The following statement confuses me a bit: 



    Bar *bar = _weakBar; 
    if(bar) 
        [self mustNotBeNil: bar]; 

Because bar is a strong reference here, the object is guaranteed to stay alive (and the variable non-nil) throughout this code. 


What exactly do you mean with "the object is guaranteed to stay alive"? I understand that bar will never he zero'ed by ARC, but why can't the referenced object be released in another thread? It's a simple assignement operation, what exactly makes it a strong reference?

Jonathan Sterling  at 2011-10-02 22:23:00:
mkseiser: It's because object pointers are implicitly  __strong. So, another way to write that would have been: 


__strong Bar *bar = _weakBar; 
if(bar) 
    [self mustNotBeNil: bar]; 

Jonathan Sterling  at 2011-10-02 22:25:04:
mkeiser: Oh, I see your question. Sure, another part of your program that isn't using ARC could erroneously release that object. But that'd be really dumb, and would probably be picked up by the static analyzer anyway. But within the context of ARC, the object  will indeed stay alive.

Jason  at 2011-10-03 02:29:47:
This article is great! One other item is to note how ARC is very particular about naming conventions, and that it will do the *wrong* thing if you break those conventions. For example:  [foo newsFeedWithObject:bar] will make ARC think you are returning a retained object since it has the 'new' prefix.

Preston  at 2011-10-03 22:12:42:
Jason: 

According to the WWDC presentation, ARC is aware of camel case and will know that -newsFeedWithObject: is not a method returning ownership. I don't have iOS developer status and so can't test this in the current 4.2 beta, however.

mikeash  at 2011-10-03 23:30:26:
Note that the naming conventions are important, but only when mixing ARC and non-ARC code. If ARC-using code calls ARC-using code, it doesn't matter what your method is named, because both sides will follow the same convention, even if it's the "wrong" one. However, when ARC calls non-ARC, or the reverse, then you definitely need to get that right.

Daniel  at 2011-10-07 15:12:39:
Once again, a nice read. 

One question, though: 

How would one silence the warning on -[NSObject performSelector:withObject:] without resorting to objc_msgSend()?

mikeash  at 2011-10-07 20:45:23:
What warnings are you referring to?

David  at 2011-10-08 00:22:08:
@Daniel When LLVM issues a warning, it typically also states the command line switch you can use to disable the warning. You can then use a pragma to turn off that warning like so: 

#pragma clang diagnostic push 
#pragma clang diagnostic ignored "
// Code that issues the warning 
#pragma clang diagnostic pop 

Keep in mind that this is not something you should do to generally kill warnings. Warnings are absolutely there for a reason, and you should take great care before you disable one. 

In general, it is likely a good idea to move away from using -performSelector* calls to arbitrary selectors if possible – using blocks or trying to switch to a set of known selectors is generally preferable. Yes, this means that you may want to rethink your target-action pattern based code. At the same time, it is sometimes the correct pattern so don't be afraid to use it if you really think it is correct.

Jean-Denis  at 2011-10-08 07:58:24:
Is there a way for debugging purposes to put a breakpoint where a giving weak reference is zeroed? Of should one rely on a watchpoint? 

Daniel  at 2011-10-08 10:50:32:
Sorry for the extreme brevity in my previous question: 
I was posting from the phone — which actually ate my more verbose first draft :-( 

@Mike When using performSelector* with a non-constant selector under ARC, you get a compiler warning that this may cause a leak — the exact warning Xcode gives you is: 
warning: performSelector may cause a leak because its selector is unknown [-Warc-performSelector-leaks,3] 
         (void)[target performSelector:selector withObject:self]; 
                       ^ 
FILENAME:LINE:COLUMN: note: used here [3] 
         (void)[target performSelector:selector withObject:self]; 
                                       ^ 
1 warning generated. 

(Which I now see how to get rid of, for the general case...) 

My particular case was a category on NSTimer — one that holds a weak reference to the actual timer-target through indirection. 
So the #pragma solution  @David mentioned would definitely be an option — although much uglier than resorting to plain old objc_msgSend(). 

I hoped using one of the CLANG macros like NS_RETURNS_NOT_RETAINED might be an option, but they seem to only be available on method declaration and not inline, at the calling site. 
Therefore, I tried defining and explicitly using the following protocol: 

@protocol WeakTimerTarget  
- (id)performSelector:(SEL)selector withObject:(id)object NS_RETURNS_NOT_RETAINED; 
@end 

But that didn't do the trick, either.

Rob Rix  at 2011-10-10 00:28:20:
It’s my understanding that __unsafe_unretained is effectively the same behaviour as “assign” for object pointers (e.g. delegates). If that is actually so, you still need to exercise caution, but it’s perhaps caution that you’re used to exercising for delegates or for breaking retain cycles. 

Mike, is that your understanding as well?

mikeash  at 2011-10-10 01:24:15:
You're correct that __unsafe_unretained is the same as a regular assignment done the old way, but most Cocoa programmers don't exercise the caution they should when using those. How much code have you seen that manually zeroes out delegate and data source pointers in a window controller's -dealloc. It's almost unheard of. And yet, it's absolutely required for correctness, because you can't know the order of object destruction, and the view might message its data source or delegate after the controller has been destroyed. In practice, this happens rarely, but it does happen, and is really annoying to track down. 

So yes, you should use the same amount of caution as dealing with both. However, that is most likely not the amount of caution that you're used to.

bill  at 2011-10-10 02:01:09:
This is iOS 5 only, right?

Roman Busyghin  at 2011-10-10 06:47:14:
I have a question about zeroing weak references. 

As you noticed earlier zeroing weak references won't work under iOS 4 and I must use __unsafe_unretained when declaring delegates, right? 

Thus this will make working with delegates in before-ARC style? Just without automatic zeroing of weak reference?

Roman Busyghin  at 2011-10-10 07:50:31:
Sorry, Mike. I have read last comments and got all the answers!

Rob Rix  at 2011-10-10 13:49:18:
Thanks for clarifying that, Mike. You’re right that I almost never see that done correctly.

Ray  at 2011-10-10 17:28:25:
I was trying to change some code (a framework) from using garbage collection to arc as I had some bugs with garbage collection (I couldn't force a specific order that objects are destroyed) and thought the more deterministic nature of ARC would benefit more.... 

However, I keep getting this error: "-fobjc-arc is not supported with fragile abi" on my project. What are the potential causes for this error? My framework uses Oracle's instant client c++ libraries so is objective-c++ as well as 32 bit only as the 64 bit instant client doesn't work on Lion yet. Does arc require 64 bit?

mikeash  at 2011-10-10 18:23:18:
bill: You can target iOS 4 and Mac OS X 10.6 with ARC, but you can't use zeroing weak references there. Those require iOS5/10.7. 

Ray: Yes, I believe ARC requires 64-bit, so you're probably out of luck. Note that the order of object destruction is something you should try to avoid relying upon anyway. Order is not guaranteed with ARC either, unless one of the objects has a strong reference to the other.

mikeash  at 2011-10-10 18:23:59:
A minor correction: ARC requires the "new runtime", which means Mac 64-bit or iOS (including simulator). Mac 32-bit is the only "old runtime" platform.

ThomasW  at 2011-10-15 15:55:35:
I've been wondering about iOS 4 compatibility. If you can't use weak zeroing references, what pattern should you follow if you want to use ARC on both iOS 4 and 5?

mikeash  at 2011-10-16 04:00:38:
My preferred route would be to use MAZeroingWeakRef wherever possible in that case. 

If you don't want the awful hackiness of that approach, you'll basically need to code like you would without ARC when it comes to weak references. Use __unsafe_unretained and be really paranoid about manually zeroing out weak references and not messaging stale pointers.

Scott  at 2011-10-18 04:10:41:
I've got a garbage-collected project that I'm converting to ARC. Despite garbage collection being set to unsupported and ARC enabled, I'm getting analysis errors as if the project is still garbage collected. Likewise, automatic conversion to ARC does not work because Xcode thinks that my project is still garbage collected. Is this my fault or a bug in Xcode?

Scott  at 2011-10-18 04:40:11:
Never mind -- I had set garbage collection to unsupported in projects, but not targets.

Sean McB  at 2011-10-28 02:11:20:
Any thoughts on converting a GC app to ARC? 

(I'm also sad that Apple seems to be ditching GC. Coding in GC one has less to worry about, though no doubt runtime performance will be somewhat better under ARC.) 

As changing the fundamental memory management model of a large codebase is not be be done lightly, I'm initially only interested in making ARC-friendly changes that are still valid in the GC case. 

Of the top of my head, my (rare) NSAllocateCollectable usage can change to malloc/free, and still work in both cases. 

Do the new strong/weak keywords work in GC? 

What does NSMakeCollectable do under ARC? What does CFBridgingRelease do under GC?

mikeash  at 2011-10-28 14:47:39:
I think the main troubles will be cycles and CF bridging. I think you can track down cycles with Instruments. 

Replacing NSAllocateCollectable will work as long as you remember to implement both  dealloc and  finalize to free the stuff, in cases where it's tied to the lifetime of an object. 

I believe weak properties already worked under GC before ARC. The  __weak qualifier definitely works. I'm not sure if strong works, but if not, retain is equivalent. 

NSMakCollectable should work under ARC the same as it does under non-ARC non-GC, which is to say that it doesn't do anything. CFBridgingRelease is an inline function so you can see how it works. Under non-ARC it just does a standard CFMakeCollectable/autorelease combination.

Mazen  at 2011-12-08 21:42:08:
Thank you for the great explanation!

Stefan  at 2011-12-11 22:03:04:
Code that uses __block to avoid retain cycles won't work anymore. Instead, use __weak as described above.


What if I need to avoid retain cycles  and allow the block to change a variable – with ARC? 

Is the __weak qualifier enough or is it necessary to use __block __weak? 

I am trying to update my cancellable NSBlockOperation which looked like this without ARC: 

__block NSBlockOperation *b = [NSBlockOperation blockOperationWithBlock:^{ 
    ... 
    If (! [b isCancelled]) ... 
}];

mikeash  at 2011-12-12 17:02:51:
As far as I know, __weak variables are still immutable when captured. For your example, I'd suggest passing the operation as a parameter to the block so it doesn't have to rely on outside variables. Another option would be to capture a proxy object to the operation rather than capturing it directly.

你可能感兴趣的:(IPhone编程)