Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weak references in blocks and retain cycles

In this question, I asked about the following code and retain cycles:

__weak Cell *weakSelf = self;
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        UIImage *image = /* render some image */
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            [weakSelf setImageViewImage:image];
        }];
    }];
    [self.renderQueue addOperation:op];

All answers state that using a weak reference here was not necessary, since this code does not result in a retain cycle. However, while experimenting with some more code, the following does result in a retain cycle (if I don't use a weak reference, the current view controller is not deallocated)

    //__weak ViewController *weakSelf = self;
    MBItem *close = [[MBItem alloc] initWithBlock:^{
        [self dismissModalWithDefaultAnimation:NO];
    }];
    NSMutableArray *items = [[NSMutableArray alloc] initWithObjects:close, nil];
    [self.childObject setItems:items];

Why would the second one result in a retain cycle but not the first one?

like image 566
Snowman Avatar asked Aug 06 '12 04:08

Snowman


People also ask

How do you avoid reference cycle?

A strong reference cycle happens when 2 instances keep a strong reference to each other. You can accidentally create such a cyclic reference, for example when working with 2-way “links” between objects, or with closures. You can break the cycle by marking a reference as weak, or by setting one of the references to nil.

What is the retain cycle?

Retain cycles: This is the state when two objects hold weak references to one another. Since the first object's reference count cannot be 0 until the second object is released, and the second object's reference count cannot be 0 until the first objet is released neither object can be released!

What is the difference between strong and weak unowned references?

The key difference between a strong and a weak or unowned reference is that a strong reference prevents the class instance it points to from being deallocated. That is very important to understand and remember. ARC keeps track of the number of strong references to a class instance.


2 Answers

Your old code creates this retain cycle if you don't use __weak:

  • (NSBlockOperation *)op retains the outer block
  • The outer block retains self (if you're not using __weak)
  • self retains (NSOperationQueue *)renderQueue
  • (NSOperationQueue *)renderQueue retains (NSBlockOperation *)op

None of the objects in that cycle can be deallocated unless one of those links is broken. But the code you showed us does break the retain cycle. When op finishes executing, renderQueue releases it, breaking the retain cycle.

I suspect that your new code creates this retain cycle:

  • (MBItem *)close retains the block
  • The block retains self
  • self retains childObject
  • childObject retains (NSMutableArray *)items
  • (NSMutableArray *)items retains (MBItem *)close

If nothing happens to break one of those links, none of the objects in the cycle can be deallocated. You haven't shown us any code that breaks the retain cycle. If there is no event that explicitly breaks it (for example by clearing out childObject.items), then you need to use __weak to break the retain cycle.

like image 80
rob mayoff Avatar answered Sep 22 '22 20:09

rob mayoff


I can't tell you the reason for the retain-cycle in your second example, because I don't know MBItem, but there are two different usage patterns with blocks.

If you expect your block to execute in any case, then you can just use self in the block:

[startSomeOperationWithCompletionBlock:^{
    [self doSomeThing];
}];

The block retains a reference to self, so that self is not deallocated before the block is executed. But after the block has executed, this reference (and the retain cycle) is gone.

If you possibly want that self is deallocated before the block has executed, or if it is possible that the block will not be called at all, then you have to use a weak reference and check the value inside the block:

__weak MyClass *weakSelf = self;
[startSomeOperationWithCompletionBlock:^{
    MyClass *strongSelf = weakSelf;
    if (strongSelf) {
        [strongSelf doSomeThing];
    }
}];

The block does not retain self in this case, so that self can be deallocated. In that case, weakSelf is set to nil automatically. Therefore, if the block is executed finally, you have to check first if weakSelf is still valid. (Or you can just use it, because sending messages to nil is a no-op.)

Assigning a strong reference strongSelf inside the block prevents self from being deallocated while the block is executing.

like image 20
Martin R Avatar answered Sep 19 '22 20:09

Martin R