3
votes

what if I'm using a dispatch_queue inside a block? What's the correct way to avoid retain cycles but also avoid releasing the weak pointers too early?

    __weak MyClass *weakSelf = self;

    [apiClient fetchData:^(...) {
        typeof(self) selfref = weakSelf;

        dispatch_async(dispatch_get_main_queue(), ^{
            // using selfref here
        }
    });

Is this the correct way to do or am I missing something? How can I ensure that everything is handled correctly and no retain cycle occurs? I can't do some dealloc-testing here...

My question is different from this here, as I do have another block inside my first block. I'm asking how to handle that situation.

3
There are plenty of questions about this. Search for them. Essential for retain cycles is the word cycle. You are in trouble if A references B and B references A. That doesn't seem the case here. You might want to check that selfref != nil (I prefer naming variables weakSelf / strongSelf which makes things clearer), apart from that the code is fine as it is.gnasher729
@swalkner check my answer here: stackoverflow.com/a/29343651/1641848Razvan

3 Answers

10
votes

First to clarify, retain cycles occur only if object points to itself! This usually happens when some property of object has strong reference back to the object(obj holds property, which holds object...) whether by block or delegate.

Blocks captures strong references to any objects passed to them from outer scope - including self. This is not problem when you declare a block and just execute it because block is declared inside a method, and ARC will dealloc it at the end of the method:

-(void)test
{
    void(^aBlock)() = ^{

        [self someMethod];

    };

    aBlock();
}

But there is a problem when you store this block for some later usage(such as callback) like this:

@property(nonatomic, copy) void(^aBlock)();

-(void)test
{
    _aBlock = ^{

        [self someMethod];

    };
}

We now had a strong reference(our property aBlock) in ClassA which holds strong reference to ClassA(via self in aBlock implementation). Now we had a problem - this class points to itself and will never be dealloced! It's even more obvious if we rewrite above as follows:

-(void)test
{
    self.aBlock = ^{

        [self someMethod];

    };
}

Now let's break appart your code just to make sure we're talking for the same things:

__weak MyClass *weakSelf = self;

[apiClient fetchData:^(...) {
    typeof(self) selfref = weakSelf;  <---- outer block

    dispatch_async(dispatch_get_main_queue(), ^{
        // using selfref here   <---- inner block
    }
                   });

Going step by step on it:

  1. We declare weakSelf, so when used in outer block it won't store strong reference to self - retainCount remain the same.
  2. We ensure that weakSelf will be available until the end of outer block by capturing it with local strong variable(if you are interested why this could happen, please note that self is neither weak nor strong - it's unsafe_unretained). This local strong variable will be dereferenced at the end of outer scope. So it only makes +1 at begin of outer block, and -1 at end - no changes outside of block.
  3. Inner block will capture strong reference to local strong reference(+1), however the inner block itself is declared inside outer block(think of it as a variable, or look at first 'test' scenario), so it'll be safely dealloced at the end of outer block. Deallocating of inner block will lead to safe dealloc of reference to local strong variable that it holds, hence -1 retain count - again no changes outside of block scope.

Hope all this makes sense to you and you'll fight gloriously with ObjC blocks ;)

0
votes

The best way to manage block by using following code.

 __weak MyClass *weakSelf = self;

    [apiClient fetchData:^(...) {
        __strong MyClass * selfref = weakSelf;

        dispatch_async(dispatch_get_main_queue(), ^{
            // using selfref here
        }
    });

Here __strong is to guarantee that if weakSelf is still alive when the first line of the block is executed, it will continue to live for the remainder of the execution of the block. so there wan't be any problem if you used that variable for inside another block. because main block scope completed when inner block execute.

Here typeof(self) selfref = weakSelf; is equal to __strong MyClass * selfref = weakSelf;

Hope this help you.

0
votes

As i know, when you use block, you should not use strong ref. But if you needed "self" in your code, you can declare weak property just like your scanerio. i think, your code work very well.

To make sure, you can easly test override dealloc method kust like below.

-(void)dealloc{
    NSLog(@"Deallocated");
}

Please copy and paste below code snippet, and than run your code. if you see "Deallocated" word in your console, you have not in retain cycles. Also you can watch from profile you are in retain cycles or not.