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:
- We declare weakSelf, so when used in outer block it won't store strong reference to self - retainCount remain the same.
- 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.
- 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 ;)