1
votes

I got a question regarding objc blocks. If you want to use self in a block you should weakify it and strongify it again in the block so you don't get into a retain cycle. In my case I also want to write a property of the class where the block exists in. Now I'm a little bit confused if this makes sense and if I ever can access this property later or if I totally loose the reference to this property.

Here's my code example:

__weak typeof(self)weakSelf = self;
void (^handleRequestBlock)(NSURLSessionDataTask*, id) = ^(NSURLSessionDataTask *task, id responseObject)
        {
            __strong typeof(weakSelf)strongSelf = weakSelf;

            if (strongSelf) {
                strongSelf->_response = [strongSelf extractResponseData:responseObject forRequestType:requestType];
                [strongSelf postSuccessNotification:strongSelf->_response];
            }
        };

First of all make this code completely sense or is there something to optimize?

Could someone maybe explain again what happens internally in objc. I read several articles now and I am more confused than before about retain cycles. As far as I know a block is an object and if it captures vars the vars are copied internally and declared as const by default as long as you don't use the __block declaration (what about properties that life in a global scope?). I still don't completely get what's the lifetime of a block and why pointers could dangling around, because the whole block object and its content should be deallocated when they finished. If someone has the time I would appreciate a nerdy and detailed answer or a link to a good reading resource! :)

Thanks in advance :)

1

1 Answers

4
votes

I want to explain 3 ways to write the block.

First: use self

void (^handleRequestBlock)(NSURLSessionDataTask*, id) = ^(NSURLSessionDataTask *task, id responseObject)
{
    self->_response = [strongSelf extractResponseData:responseObject forRequestType:requestType];
    [weakSelf postSuccessNotification:self->_response];
};

the block handleRequestBlock retain self, if self has a property that owns the block, there will be a retain circle. The block will keep self retained until you release the block. So if you release the block after call the block (set the block to nil to release it), the cycle will not exist.

Note: Most implementation will not release the block after call it, because we may need it afterwards, so the retain circle will exist all the time.

Second: Use weak self

__weak typeof(self)weakSelf = self;
NSLog(@"%p", &weakSelf) ;
void (^handleRequestBlock)(NSURLSessionDataTask*, id) = ^(NSURLSessionDataTask *task, id responseObject)
{
    NSLog(@"%p", &weakSelf) ;
    weakSelf->_response = [strongSelf extractResponseData:responseObject forRequestType:requestType];
    [weakSelf postSuccessNotification:weakSelf->_response];
};

the block will not retain self and if the instance of self is deallocated, weakSelf is nil.

More about this example: I add two lines of log to show that : the address of variable weakSelf out of the block scope and inside the block scope are different. Because weakSelf is a stack local and __weak variable, so block capture it with a variable that has same value of weakSelf but not send retain message to the instance it indicates, weakSelf here is another variable with different address.

Third: retain self only when it is needed.

__weak typeof(self)weakSelf = self;
void (^handleRequestBlock)(NSURLSessionDataTask*, id) = ^(NSURLSessionDataTask *task, id responseObject)
{
    __strong typeof(weakSelf)strongSelf = weakSelf;

    if (strongSelf) {
        strongSelf->_response = [strongSelf extractResponseData:responseObject forRequestType:requestType];
        [strongSelf postSuccessNotification:strongSelf->_response];
    }
};

There is a disadvantage on the second way: If self is not deallocated when the block is executing the first line of code, after executing this line of code , the instance of self is deallocated (because we didn't retain it, it may be sent release method on other thread and it will be deallocated), at the second line of code , weakSelf is nil, so something bad will happen, it depends on the logic of your code.

So the third way solved that, it only retain self when the block is executing and release self at the end of the block (release means decrease retain count by 1).

More Links :

Working with blocks

A look inside blocks

How do i avoid caturing self in blocks...