1

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 :)

slngr
  • 13
  • 3

1 Answers1

4

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

Community
  • 1
  • 1
KudoCC
  • 6,912
  • 1
  • 24
  • 53
  • In my code example does the ivar _response of my _original_ self reference get the return value of the function, or is it assigned to a copied version of the original instance of self? Still try to get where objects get copied/newly created an where the references to the original object are kept. – slngr Sep 03 '14 at 12:55
  • @slngr Can this [link](http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-1/) help ? – KudoCC Sep 04 '14 at 01:59
  • @slngr I'm sorry I don't understand what you mean. You should know that: The variable with `__weak` that is used in the block won't be sent `retain` method, you can deduce it from my example 2. Once more, `__strong typeof(weakSelf)strongSelf` `strongSelf` will only `retain` `self` when the block get called and `weakSelf` is not nil then. – KudoCC Sep 04 '14 at 02:39
  • Sorry I'm really bad in explaining ;) I totally got the __weak and __strong thingy. I just wanted to know what happens internally when a block gets created. If variables are captured by the block they get by default declared as const (as long as they're not declared as __block). But are the captured vars const references or copies of the objects? What gets on the stack, what on the heap? And as far as I understand right vars that are captured by a block are visible to all other living blocks?! And big thanks for explaining! – slngr Sep 04 '14 at 06:50
  • @slngr block is a struct, in the struct, there is a place to store the vars it captures. If the var is a global var, block will store the address of vars in the struct, if it is a local var, block store the value of it, if the vars isn't __block, the var in block struct will be marked readonly. To answer your last question, whether the vars captured by a block is visible to all other living blocks also depends on the scope of vars. If it is a local, the answer is not. If the var is global, all block can access it. – KudoCC Sep 04 '14 at 07:29
  • @slngr The vars on the stack will be invalid after leaving its scope, the vars on the heap will be valid all along until the memory it placed is freed. When a block is copied, the struct of block will be placed on the heap because the block should stay valid so it can be used afterwards. – KudoCC Sep 04 '14 at 07:34