9

Why is it necessary to have a strong reference to a weak reference inside a block?

I understand that having a weak reference inside the block will avoid retain cycles. But why must there be a strong reference to the weak one again?

Background:
As described by Mason this is best practice.

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[...]

Example:

__weak typeof(self) weakSelf = self;
void (^someBlock)(id) = ^(id data){
    typeof(self) strongSelf = weakSelf;
    // code using strongSelf
});
Community
  • 1
  • 1
lukas_o
  • 3,776
  • 4
  • 34
  • 50
  • it is not really necessary to make a pointer strong again inside the block. – holex May 22 '14 at 12:51
  • 1
    A much more beautiful and clean way is using `@weakify(self)` and `@strongify(self)` if you use [libextobj](https://github.com/jspahrsummers/libextobjc) – lukas_o Feb 25 '15 at 16:12
  • 1
    it might be more beautiful, but that is not part of the official language. – holex Feb 26 '15 at 08:32
  • @holex "it is not really necessary to make a pointer strong again inside the block". Why? the weak pointer might go nil, in mid of the block execution. – BangOperator Sep 08 '17 at 05:34
  • @BangOperator, so what happens then? if the actual object is about being released anyway then block also loses its purpose in the most of the cases. – holex Sep 08 '17 at 10:05
  • @holex Yes "in the most of the cases". Suppose in block I call [weakSelf methodA] and then [weakSelf methodB], and they change state some shared BOOL A from false to true and some shared BOOL B from false to true. weak Self might leave BOOL A as true and B as false. – BangOperator Sep 08 '17 at 10:37
  • @BangOperator, yes, that is a kinda scenario which is going to happen in practice at all, like ever; but obviously that always is up to the chosen design-patterns and architectures, I have seen some mental ones, like the hypothetical situation you just described – so, I got the gist. – holex Sep 08 '17 at 10:44

2 Answers2

15

Imagine that the last remaining strong reference to self is held on a different thread to the one that your block runs on.

Now this happens:

__weak typeof(self) weakSelf = self;
void (^someBlock)(id) = ^(id data){
    if (weakSelf != nil) {
       // last remaining strong reference released by another thread. 
       // weakSelf is now set to nil.
       [myArray addObject:weakSelf];
    }
});

This will crash with an NSInvalidArgument exception for adding nil to an array.

Making the reference strong before use removes the potential race condition and ensures that the pointer will always point to the same object.

If you are 100% certain that an object will only ever be referenced by one thread, it isn't strictly necessary to do this. But it's bad practice to make that assumption.

Chris Devereux
  • 5,453
  • 1
  • 26
  • 32
  • 1
    if you let the application to crash in those scenarios, it would help you to find the bug eaiser of why and where you released the object _before_ in spite of any of the blocks still would need it. – holex May 22 '14 at 12:58
  • 1
    There are legitimate reasons for allowing an object to be deallocated while holding a weak reference on another thread. You *may* want to assert that the pointer is not nil, of course, but doing so on weakSelf would suffer from the same race condition. – Chris Devereux May 22 '14 at 13:01
  • 2
    If the sole reason for using weak reference was to avoid a retain cycle, then any circumstance in which the weak reference becomes nil before or during block execution arguably represents an error. – ipmcc May 22 '14 at 19:35
  • In that case you still want to fail predictably. Introducing race conditions doesn't improve the debugging experience... – Chris Devereux May 22 '14 at 21:35
  • @ChrisDevereux You are the MAN ! so many explanations and this one is the most optimal ! Is this true for Swift as well? Where we use the capture list? if yes, please tell how to convert [weak self] to self – nr5 Oct 21 '18 at 10:33
6

It's not inherently necessary, but the general idea is to make sure that the object pointed to by weakSelf is not dealloc'ed while the block is executing. Creating the strong reference has the side effect of retaining the object. That retain will be released by ARC when the strong reference goes out of scope. It's largely defensive. Generally speaking, you should aim to provide other (better) guarantees that your system remains stable during block execution.

ipmcc
  • 29,581
  • 5
  • 84
  • 147