2
votes

I was always using __weak references to self when I was in a block in GCD. Everyone recommends that. I know that strong reference to self (tested) in GCD can't produce retain cycles. Apple recommends using a __weak reference to self and then a __strong reference to that _week reference to guarantee that self won't be nil when the block is executed.

I have the following piece of code:

- (IBAction)startGCD:(id)sender {

    GCDVC* __weak weakSelf = self;

    [self.activityIndicator startAnimating];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1

        // VC2* __strong strongSelf = weakSelf;

        [weakSelf.proArray addObject:@"2"];

        [NSThread sleepForTimeInterval:10];

        NSLog(@"%@",weakSelf.proArray);

        dispatch_async(dispatch_get_main_queue(), ^{ // 2
            [weakSelf.activityIndicator stopAnimating];
        });
    });

}

Test 1 I press the button and the Indicator is spinning. I press the back button before the GCD finish and the GCDViewController is being released.

Test 2 Then I uncomment the strong reference to self and I execute the same procedure. The GCDViewController is not released until the block has finished it work. But then its released.

Test 3 Now if I refer directly to self (without __weak or __strong) I have the exact behaviour with Test 2.

So if I want to make sure that the self won't be nil when the block is executed what's the point of using a __strong reference to a __weak reference to self? Do I miss something here? Is any example that would change the result of Test 2 & 3?

1
You create a strong reference inside the block to prevent any scenario where self gets deallocated while the block is running.dan
Yes I know that. But what is the difference of __strong to self from calling self directly? If I call self directly I increase the retain I count like I do with __strong. And the output is the same on both casesBlackM
If you refer to self directly in the block then it prevents self from being deallocated for as long as block is around. Creating weak self outside the block and then the strong self inside of it allows self to be deallocated while the block is around and only prevents it while the block is running.dan
The difference is irrelevant in your example because your block executes immediately. It would matter in a case where a reference to the block is being held on to by something and then executed laterdan
You mean a simple block but not in GCD? For example when a block is assigned in a strong property?BlackM

1 Answers

6
votes

I was always using __weak references to self when I was in a block in GCD. Everyone recommends that.

No. I don't know anyone that recommends that. Weak references make sense in some situations where there would be retain cycles. But whether a block should capture a weak or strong reference to any object (including the one self points to) depends on the particular memory management design of the code in question and

I know that strong reference to self (tested) in GCD can't produce retain cycles.

Passing a block to dispatch_async on a global or main queue will NEVER produce a retain cycle.

Now if I refer directly to self (without __weak or __strong) I have the exact behaviour with Test 2.

Maybe you don't see any difference in this case. But they have different behavior in general. Basically, your question is what is the difference between the block capturing a strong or weak reference to the object pointed to by self.

The difference is in what happens, if the current object (object pointed to by self) has been released by every other object that holds a strong reference to it, except potentially the block, and the block is executed afterwards asynchronously.

  • If the block captured a strong reference to the current object, then that reference would keep the object alive during this whole time. So the object is still alive and we can use it and send messages to it, etc.
  • If the block captured a weak reference to the current object, then the current object has already been deallocated, when the last strong reference to it was released. So the block's weak reference would be nil at this point, and the block is not going to do anything with it (it is either going to check strongSelf and return when it sees that it is nil, or it will send messages to strongSelf which is nil which does nothing).

So in one case stuff is done and in the other case nothing is done.

If the current object in your case is some UI element, like a view or view controller, then as long as it is in the view hierarchy, it is going to be retained by things in the view hierarchy, so you need to exit out of this view/view controller in order to have the situation where nothing else has a strong reference to it.

In your case, all you do inside the block is [weakSelf.activityIndicator stopAnimating];, i.e. a change to the UI. It doesn't matter whether this command is run or not, because, remember, the view containing this has already stopped displaying. Nobody can see this activity indicator. So what difference does it make if you stop it or not? But this is specific to this case. In general, you can imagine that the command inside the block changes some global state, or sends something over the network or something, where there is a big difference between if you do it or not.