I am a little confused about block usage in Objective-C. I currently use ARC and I have quite a lot of blocks in my app, currently always referring to self
instead of its weak reference. May that be the cause of these blocks retaining self
and keeping it from being dealloced ? The question is, should I always use a weak
reference of self
in a block ?
-(void)handleNewerData:(NSArray *)arr
{
ProcessOperation *operation =
[[ProcessOperation alloc] initWithDataToProcess:arr
completion:^(NSMutableArray *rows) {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateFeed:arr rows:rows];
});
}];
[dataProcessQueue addOperation:operation];
}
ProcessOperation.h
@interface ProcessOperation : NSOperation
{
NSMutableArray *dataArr;
NSMutableArray *rowHeightsArr;
void (^callback)(NSMutableArray *rows);
}
ProcessOperation.m
-(id)initWithDataToProcess:(NSArray *)data completion:(void (^)(NSMutableArray *rows))cb{
if(self =[super init]){
dataArr = [NSMutableArray arrayWithArray:data];
rowHeightsArr = [NSMutableArray new];
callback = cb;
}
return self;
}
- (void)main {
@autoreleasepool {
...
callback(rowHeightsArr);
}
}
You don't have to always use a weak reference. If your block is not retained, but executed and then discarded, you can capture self strongly, as it will not create a retain cycle. In some cases, you even want the block to hold the self until the completion of the block so it does not deallocate prematurely. If, however, you capture the block strongly, and inside capture self, it will create a retain cycle.
I totally agree with @jemmons.
"But this should not be the default pattern you follow when dealing with blocks that call self! This should only be used to break what would otherwise be a retain cycle between self and the block. If you were to adopt this pattern everywhere, you'd run the risk of passing a block to something that got executed after self was deallocated."
To over come this problem one can define a strong reference over the weakSelf inside the block.
As Leo points out, the code you added to your question would not suggest a strong reference cycle (a.k.a., retain cycle). One operation-related issue that could cause a strong reference cycle would be if the operation is not getting released. While your code snippet suggests that you have not defined your operation to be concurrent, but if you have, it wouldn't be released if you never posted
isFinished
, or if you had circular dependencies, or something like that. And if the operation isn't released, the view controller wouldn't be released either. I would suggest adding a breakpoint orNSLog
in your operation'sdealloc
method and confirm that's getting called.You said:
The retain cycle (strong reference cycle) issues that occur with blocks are just like the retain cycle issues you're familiar with. A block will maintain strong references to any objects that appear within the block, and it will not release those strong references until the block itself is released. Thus, if block references
self
, or even just references an instance variable ofself
, that will maintain strong reference to self, that is not resolved until the block is released (or in this case, until theNSOperation
subclass is released.For more information, see the Avoid Strong Reference Cycles when Capturing self section of the Programming with Objective-C: Working with Blocks document.
If your view controller is still not getting released, you simply have to identify where the unresolved strong reference resides (assuming you confirmed the
NSOperation
is getting deallocated). A common example is the use of a repeatingNSTimer
. Or some customdelegate
or other object that is erroneously maintaining astrong
reference. You can often use Instruments to track down where objects are getting their strong references, e.g.:Or in Xcode 5:
Some explanation ignore a condition about the retain cycle [If a group of objects is connected by a circle of strong relationships, they keep each other alive even if there are no strong references from outside the group.] For more information, read the document
This is how you can use the self inside the block:
//calling of the block
It helps not to focus on the
strong
orweak
part of the discussion. Instead focus on the cycle part.A retain cycle is a loop that happens when Object A retains Object B, and Object B retains Object A. In that situation, if either object is released:
Thus, those two objects will just hang around in memory for the life of the program even though they should, if everything were working properly, be deallocated.
So, what we're worried about is retain cycles, and there's nothing about blocks in and of themselves that create these cycles. This isn't a problem, for example:
The block retains
self
, butself
doesn't retain the block. If one or the other is released, no cycle is created and everything gets deallocated as it should.Where you get into trouble is something like:
Now, your object (
self
) has an explicitstrong
reference to the block. And the block has an implicit strong reference toself
. That's a cycle, and now neither object will be deallocated properly.Because, in a situation like this,
self
by definition already has astrong
reference to the block, it's usually easiest to resolve by making an explicitly weak reference toself
for the block to use:But this should not be the default pattern you follow when dealing with blocks that call
self
! This should only be used to break what would otherwise be a retain cycle between self and the block. If you were to adopt this pattern everywhere, you'd run the risk of passing a block to something that got executed afterself
was deallocated.