0

我正在研究从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属性(copyied )保留块,而块保留它,networkFetcher因为它在中使用它NSLog

现在,要中断块,NetworkFetcher必须将完成处理程序设置为完成nil下载已请求的数据。

// in NetworkFetcher.m class
- (void)requestCompleted {

    if(self.completionHandler) {
        // invoke the block
        self.completionHandler();
    }

    self.completionHandler = nil;
}

好的。这样就没有保留周期了。该块在运行时,它会释放其对 的引用,networkFetchernetworkFetcher产生nil对块的引用。

现在,我的问题是关于片段的执行流程。以下操作顺序是否正确?

  1. networkFetcher运行完成处理程序
  2. 该块被执行
  3. 该块释放对networkFetcher
  4. networkFetcher释放对块的引用

我的怀疑取决于行动 3) 和 4) 。如果 3) 在 4) 之前执行,则没有人引用networkFetcher,因此可以在任何执行时间释放它(ARC 将在 结束时发出释放调用downloadData)。我错了还是我错过了什么?

希望问题很清楚。

4

2 回答 2

3
// in NetworkFetcher.m class
- (void)requestCompleted {

    if(self.completionHandler) {
        // invoke the block
        self.completionHandler();
    }

    self.completionHandler = nil;
}

该块在设置为 nil之前被执行。在此方法中,块的执行是同步的——在它完成执行之前不会发生任何事情。请记住,块的存在并不意味着其中的代码将异步执行。

该块在执行后不会释放它的引用,因为该块仍然作为网络提取器实例的属性存在。如果你有点奇怪,你可以再次执行它。

该块仅在释放它时释放它捕获的对象 - 这发生在 completionHandler 属性设置为 nil 时,即在块执行之后。

于 2014-04-19T16:03:30.217 回答
1

步骤更像这样:

  1. 网络获取器完成请求,然后调用 Block。
  2. 块执行

没有其他事情发生。由于存在保留周期,因此网络提取器不会解除分配。

如果网络提取器在调用它之后将其完成块 ivar显式设置为,您将得到以下信息:nil

最初,完成块的引用计数为 +1。

  1. 网络获取器完成请求,然后调用 Block。
  2. 该块异步执行。(块的释放计数等于 +1)。
  3. 网络提取器将块设置为 nil(版本 -1)
  4. 块完成(释放-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;
    }];
}
于 2014-04-19T15:01:33.420 回答