Search code examples
iosiphonexcodeautomatic-ref-countingweak-references

When and why would I want to declare a local variable as __weak using ARC?


Mike Ash has written this introduction to ARC where he introduces something like:

__weak Foo *_weakFoo = [object foo];

Why would I want to do that for a local, temporary variable? __weak is a zeroing reference which will set the _weakFoo pointer automatically to nil as soon as the referenced object gets deallocated. Also, __weak is only available in iOS >= 5.

When would I run into trouble when I simply do this?:

Foo *_weakFoo = [object foo];

This is always expected to return an object or nil. My guess is this:

Foo *_weakFoo = [object foo];
[self doSomethingStupid]; // does something bad so foo gets deallocated
[_weakFoo doIt]; // CRASH! msg sent to deallocated instance 0x123456

One thing that still bugs me with ARC is: When does it know that I don't need an object anymore? I'd argue that when I set a pointer to nil or to something else it figures out that the previously referenced object isn't needed by this owner anymore and therefore maybe can go away. But the point is: I set it to nil. So it's nil anyways!

So when would __weak for a local variable make sense, and what kind of crazy thing must I do somewhere else so that I really need that?


Solution

  • I use __weak local variables if I have to manipulate self inside of a block to avoid a retain cycle. Consider this example where I'm using GCD and blocks to perform a network request for a string, and then setting it on a label declared by the class, in this case, TurtlesViewController.

    __weak TurtlesViewController *weakSelf = self;
    dispatch_queue_t networkQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(networkQueue, ^{
    
        // Kick off a network task for some data that is going to populate a label declared on our class
        NSString *returnString = [networkDataSource retrieveTurtleTime];
    
        // Dispatch back to the main thread to populate the UILabel
        dispatch_async(dispatch_get_main_queue(), ^{
    
            // Using self.label here creates a retain cycle. Self owns the block and the block has captured self
            self.label.text = returnString;
    
            // Instead, we use weakSelf for our reference to the label as it will be torn down by ARC at the end of the loop.
            weakSelf.label.text = returnString;
        });
    });