5

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?

Radar3
  • 55
  • 6
openfrog
  • 40,201
  • 65
  • 225
  • 373

1 Answers1

9

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;
    });
});
Mark Adams
  • 30,776
  • 11
  • 77
  • 77
  • 1
    How could there be a retain cycle though? – openfrog Jan 10 '12 at 16:24
  • 1
    @openfrog - This particular block might not be at the greatest risk of being a retain cycle, but one that I've run into would be block-based observers for notifications (using NSNotificationCenter's `-addObserverForName:object:queue: usingBlock:`). If you set up such an observer in an object, and refer to something on `self`, you'll set up a cycle as the object holds on to the block and the block holds on to the object. – Brad Larson Jan 10 '12 at 21:17
  • 2
    After writing this I realized that this isn't the best example because dispatch_async() copies the block, but only until the end of the method. A better example would have been using NSBlockOperation since an instance of such owns the passed in block for the lifetime of the object, making retain cycles more likely. – Mark Adams Jan 11 '12 at 01:07