I'm studying a code snippet I grabbed from Effective Objective-C book by Matt Galloway. The snippet is the following (I've modified a little bit).
- (void)downloadData {
NSURL *url = // alloc-init
NetworkFetcher *networkFetcher =
[[NetworkFetcher alloc] initWithURL:url];
[networkFetcher startWithCompletionHandler:^(NSData *data){
NSLog(@"Request URL %@ finished", networkFetcher.url);
_fetchedData = data;
}];
// ARC will put a release call for the networkFetcher here
}
As stated by the author, such pattern is used by different networking libraries and there is a retain cycle. The retain cycle is quite obvious for me since, if you think in terms of object graph, the networkFetcher
instance retains the block through a completionHandler
property (copy
ied), while the block retains the networkFetcher
since it uses it in NSLog
.
Now, to break the block, the NetworkFetcher
must set the completion handler to nil
when it finishes to download the data has been requested.
// in NetworkFetcher.m class
- (void)requestCompleted {
if(self.completionHandler) {
// invoke the block
self.completionHandler();
}
self.completionHandler = nil;
}
Ok. In this way there is no retain cycle anymore. The block, when run, it frees its reference to the networkFetcher
and the networkFetcher
makes nil
the reference to the block.
Now, my question regards the execution flow of the snippet. Is the following sequence of actions correct?
- the
networkFetcher
runs the completion handler - the block is executed
- the block frees the reference to the
networkFetcher
- the
networkFetcher
release the reference to the block
My doubt relies on actions 3) and 4) . If 3) is executed before 4) no one has a reference to networkFetcher
and so it can be released at any execution time (ARC will put a release call at the end of downloadData
). Am I wrong or am I missing something?
Hope the question it's clear.