iOS blocks and strong/weak references to self

22,202

Solution 1

You don’t need to make two sets of weak references. What you want to avoid with blocks is a retain cycle—two objects keeping each other alive unnecessarily.

If I have an object with this property:

@property (strong) void(^completionBlock)(void);

and I have this method:

- (void)doSomething
{
    self.completionBlock = ^{
        [self cleanUp];
    };

    [self doLongRunningTask];
}

the block will be kept alive when I store it in the completionBlock property. But since it references self inside the block, the block will keep self alive until it goes away—but this won’t happen since they’re both referencing each other.

In this method:

- (void)doSomething
{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [self cleanUp];
    }];

    [self doLongRunningTask];
}

you don’t need to make a weak reference to self. The block will keep self alive, since it references self from within, but since all we’re doing is handing the block off to [NSOperationQueue mainQueue], self isn’t keeping the block alive.

Hope this helps.

Solution 2

Both constructs are fine. It just depends upon your intent. What do you want to happen if the object is (a) released after the outer block commences but (b) before the inner block starts on the main queue? If you do not want it retained in this scenario (which I might guess was your intent, given that you're going through this weakSelf exercise in the first place), then use your final example, where you have the second weak pointer. Otherwise you can use your other example.

Having said that, a couple of observations:

  1. It's not a forgone conclusion that you have to use this weakSelf pattern in the first place. Some people mistakenly think that they have to use this weakSelf pattern to avoid a strong reference cycle (a.k.a. retain cycle). But this code sample does not constitute a strong reference cycle. It simply retains the object while the dispatched code executes, which is a very different consideration.

    In fact, sometimes you need/want that. Sometimes you don't. It depends upon the business problem you're solving. Absolutely, you frequently don't want it to keep a strong reference to self, in which case the weakSelf pattern makes perfect sense. But that's not always the case.

    But my point is that you shouldn't be pursing this weakSelf pattern (at least in this dispatch_async scenario) to avoid a strong reference cycle. No such cycle exists. Where this is an issue is where you have a block variable (e.g. some completionHandler block). In that case, the weakSelf pattern is critical. But not here.

  2. But let's consider for a second that scenario in which you don't want self retained. Then there's a question of whether you want the dispatched code continuing at all in the first place. If not, maybe you should be using a operation queue with cancelable operations instead of GCD.

    For example, I'm surprised how often people agonize over whether they're going to retain the view controller while some background network request is running, but don't worry about whether they should be canceling that background network request in the first place. Often, the latter is a far more significant design consideration (e.g. the PDF or image you're downloading takes up far more system resources (both memory and network bandwidth) than the view controller ever will).

  3. But let's assume that (a) you really want the dispatched code to continue to execute, but (b) you don't want to retain self. (This seems like a rare scenario, but it's the one you've asked about, so let's pursue that.) The final question of whether you need your strongSelf construct, also. In your case, where you're just calling a single method of self, you don't need to bother with this strongSelf construct. That's critical only if you're going to deference ivars or otherwise need to avoid race conditions. But, in this example, given that a message sent to a nil object does nothing, you technically often don't need to worry about this strongSelf construct at all.

Don't get me wrong. It's good to get one's arms around the weakSelf pattern, as well as the nested strongSelf pattern that sometimes accompanies it. I'm just suggesting it's good to understand when these patterns are truly needed. And I think the choice of GCD versus a cancelable NSOperation is often a far more critical, but often overlooked, question.

Solution 3

Blocks are created and stored on the stack. So the block will be destroyed when the method that created the block returns.

If a block becomes an instance variable ARC copy the block from the stack to the heap. You can explicit copy a block with the copy message. Your block is now a heap-based block instead of a stack-based block. And you have to deal with some memory management issues. The block itself will keep a strong reference to any objects it references. Declare __weak pointers outside the block and then reference this pointer within the block to avoid retain cycles.

Share:
22,202
Mason
Author by

Mason

Updated on July 14, 2022

Comments

  • Mason
    Mason almost 2 years

    I have a question about strong and weak references to self in blocks in iOS. I know the proper way to refer to self inside a block is to create a weak reference outside the block, and then a strong reference to that weak reference inside the block, like this:

    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
        typeof(self) strongSelf = weakSelf;
        NSLog(@"%@", strongSelf.someProperty);
    });
    

    However, what happens if you have nested blocks? Is the one set of references enough? Or do you need a new set for each block? For example, which of the following is correct?

    This:

    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
        typeof(self) strongSelf = weakSelf;
        NSLog(@"%@", strongSelf.someProperty);
        dispatch_async(dispatch_get_main_queue(), ^ {
            strongSelf.view.frame = CGRectZero;
        });
    });
    

    Or this:

    __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
            typeof(self) strongSelf = weakSelf;
            NSLog(@"%@", strongSelf.someProperty);
            __weak typeof(strongSelf) weakSelf1 = strongSelf;
            dispatch_async(dispatch_get_main_queue(), ^ {
                typeof(strongSelf) strongSelf1 = weakSelf1;
                strongSelf1.view.frame = CGRectZero;
            });
        });
    

    Any information or explanation is much appreciated!