我正在研究我从 Matt Galloway Effective Objective-C 一书中获取的代码片段。该片段如下(我已经修改了一点)。
- (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
}
如作者所述,这种模式由不同的网络库使用,并且存在保留周期。保留周期对我来说非常明显,因为如果你从对象图的角度考虑,networkFetcher
实例通过completionHandler
属性(copy
ied)保留块,而块保留networkFetcher
,因为它在NSLog
中使用它。
现在,为了打破阻止,NetworkFetcher
必须在完成下载已请求的数据后将完成处理程序设置为nil
。
// in NetworkFetcher.m class
- (void)requestCompleted {
if(self.completionHandler) {
// invoke the block
self.completionHandler();
}
self.completionHandler = nil;
}
确定。这样就不再有保留周期了。该块在运行时会释放对networkFetcher
的引用,networkFetcher
使nil
引用该块。
现在,我的问题是关于代码段的执行流程。以下一系列操作是否正确?
networkFetcher
运行完成处理程序networkFetcher
networkFetcher
释放对块的引用我的怀疑依赖于行动3)和4)。如果3)在4)之前执行,则没有人引用networkFetcher
,因此可以在任何执行时释放它(ARC将在downloadData
结束时发布释放调用)。我错了还是错过了什么?
希望问题清楚。
答案 0 :(得分:3)
// in NetworkFetcher.m class
- (void)requestCompleted {
if(self.completionHandler) {
// invoke the block
self.completionHandler();
}
self.completionHandler = nil;
}
在设置为nil之前执行块。在此方法中块的执行是同步的 - 在完成执行之前不会发生任何事情。请记住,块的存在 not 意味着内部的代码将异步执行。
一旦执行该块,它就不会释放它的引用,因为该块仍然作为网络提取器实例的属性存在。如果你有点奇怪,你可以再次执行它。
该块仅释放它在解除分配时捕获的对象 - 当completionHandler属性设置为nil(即块执行后)时会发生这种情况。
答案 1 :(得分:1)
步骤更像是这样:
没有其他事情发生。由于存在保留周期,因此网络提取器无法解除分配。
IFF网络提取器显式在调用它之后将其完成块ivar设置为nil
,你得到这个:
最初,完成块的引用计数为+1。
networkFetcher
指针)。还有一些其他方法可以阻止保留周期:
- (void)downloadData {
NSURL *url = // alloc-init
NetworkFetcher *networkFetcher =
[[NetworkFetcher alloc] initWithURL:url];
[networkFetcher startWithCompletionHandler:^(NSData *data){
NSLog(@"Request URL %@ finished", url);
_fetchedData = data;
}];
}
- (void)downloadData {
NSURL *url = // alloc-init
NetworkFetcher *networkFetcher =
[[NetworkFetcher alloc] initWithURL:url];
__block NetworkFetcher* blockNetworkFeatcher = networkFetcher;
[networkFetcher startWithCompletionHandler:^(NSData *data){
NSLog(@"Request URL %@ finished", blockNetworkFeatcher.url);
_fetchedData = data;
blockNetworkFeatcher = nil;
}];
}
- (void)downloadData {
NSURL *url = // alloc-init
NetworkFetcher *networkFetcher =
[[NetworkFetcher alloc] initWithURL:url];
__weak NetworkFetcher* weakNetworkFeatcher = networkFetcher;
[networkFetcher startWithCompletionHandler:^(NSData *data){
NSLog(@"Request URL %@ finished", weakNetworkFeatcher.url);
_fetchedData = data;
}];
}