Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens to weakSelf and strongSelf in a block when the same block is executed multiple times?

PREFACE

According to the Clang docs, "For __weak objects, the current pointee is retained and then released at the end of the current full-expression." Which to me indicates that if I do this:

__weak typeof(self) weakSelf = self;
[self doSomethingInBackgroundWithBlock:^{
    if (weakSelf) {
        [weakSelf doSomethingInBlock];
    }
}];
NOTE

If you reference @dasblinkenlight's answer below, you'll notice that there is a possibility of weakSelf becoming nil before doSomethingBlock.

Assuming doSomethingInBlock, does start with weakSelf existing, the rest of it should run no problem and there's no risk of weakSelf becoming nil before it has finished executing. However, if I were to run this:

__weak typeof(self) weakSelf = self;
[self doSomethingInBackgroundWithBlock:^{
    if (weakSelf) {
        // Guaranteed to be retained for scope of expression
        [weakSelf doSomethingInBlock];
        // weakSelf could possibly be nil before reaching this point
        [weakSelf doSomethingElseInBlock];
    }
}];

The work around that is suggested is to take weakSelf and convert it to a strong variable inside of the block, like this:

__weak typeof(self) weakSelf = self;
[self doSomethingInBackgroundWithBlock:^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        [strongSelf doSomethingInBlock];
        [strongSelf doSomethingElseInBlock];
    }
}];

QUESTION

What happens to the weakSelf and strongSelf during multiple iterations of the same block? Is there a chance that in processBlock()(below), self could exist for some objects and not for others?

EXAMPLE

For example, if I were processing an array of objects in the background using something like this, where processBlock contains references to self:

- (void) processValuesInBackgroundWithArray:(NSArray *)array usingBlock:(void (^)(id))processBlock {
    for (id ob in array) {

        // Block is called for each Object

        // Is there a chance that self will exist for some objects and not for others?
        processBlock(ob);
    }
}

Called like this:

__weak typeof(self) weakSelf = self;
[self processValuesInBackgroundWithArray:someArray usingBlock:^(id object) {
    __strong typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        [self doSomethingWithObject:object];
        [self doSomethingElseWithObject:object];
    }      
}];

So the block references strongSelf from weakSelf, but the block is executed multiple times. Is there a chance that strongSelf might become nil in between iterations of objects in an array?

like image 877
Logan Avatar asked May 19 '14 17:05

Logan


1 Answers

There is no guarantee that in your first example the weakSelf inside the if would be non-nil, because the block has two full expressions that reference it:

  • the if (weakSelf) check is the first full expression
  • the weakSelf in [weakSelf doSomethingInBlock]; invocation is the second one.

Therefore, your trick with strongSelf should be applied even when there is only one weakSelf invocation "protected" by the if statement.

Is there a chance that in processBlock()(below), self could exist for some objects and not for others?

Since there is no guaranteed __strong reference in a stack frame preceding the processValuesInBackgroundWithArray: call, self may become released between iterations of the loop, but only in a situation when the call of your last code snippet happens on a __weak or an unretained reference to the object containing the method from your last snippet.

Let's say that your last code snippet is inside a method called testWeak, in a class called MyClass:

-(void)testWeak {
    __weak typeof(self) weakSelf = self;
    [self processValuesInBackgroundWithArray:someArray usingBlock:^(id object) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (strongSelf) {
            [self doSomethingWithObject:object];
            [self doSomethingElseWithObject:object];
        }      
    }];
}

When the call is made like this

[myClassObj testWeak];

and myClassObj is __strong, the self object inside testWeak would be retained outside the call by the strong reference to myClassObj, so your code would be good, with or without the strongSelf trick.

When myClassObj is weak, however, and the last __strong reference gets released concurrently with the running loop, some objects inside the loop would end up seeing a nil weakSelf inside the block. The only difference that the strongSelf is going to make is preventing that doSomethingElseWithObject would be called on nil, while doSomethingWithObject would be called on a non-nil object.

like image 57
Sergey Kalinichenko Avatar answered Sep 28 '22 08:09

Sergey Kalinichenko