简体   繁体   English

网络完成块,递归和ARC保留周期

[英]Networking completion block, recursion and ARC retain cycle

Here is a tricky question, to which the answer might prove useful for many networking apprentice out there, including me. 这是一个棘手的问题,答案可能对许多网络学徒有用,包括我。

Some background information on the context: 关于上下文的一些背景信息:

  • Let's say you want to download data from an Online Service 假设您要从在线服务下载数据
  • you want to do this asynchronously 你想异步这样做
  • you want to do one download at a time, then maybe do another one upon completion of the previous one. 你想一次下载一个,然后在完成前一个下载后再做一个下载。

One neat way of doing this is using recursion. 一种巧妙的方法是使用递归。 The problem with common implementations you can come up with is Retain cycles, between networking completion blocks and self. 您可以提出的常见实现的问题是网络完成块和自身之间的保留周期。 This can be solved using weakSelf reference pointers. 这可以使用weakSelf参考指针来解决。

But, what about retain cycles for recursive calls ? 但是,保留递归调用的周期呢?

We have implemented a recursive stack, self pointing to a download management class, like this: 我们已经实现了一个递归堆栈,自我指向下载管理类,如下所示:

-(void)startNetworkDownloadForObjectAtIndex:(int) anIndex
{
    __typeof__(self) __weak weakSelf = self;
    NSURL *urlForObjectAtIndex = [SomeClass URLforIndex:anIndex];
    [self.downloadManager getResourceAtURL:urlForObjectAtIndex success:^(AFHTTPRequestOperation *operation, id responseObject) {
                               if (indexOfObjectToDownload < weakself.totalNumberOfObjectsToDownload) [weakSelf startNetworkDownloadForObjectAtIndex:indexOfObjectToDownload+1];
                               else [weakSelf startDOwnloadTimer]; 
                            }
                            failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                // response code is in operation.response.statusCode
                                [weakSelf handleNetworkError:error];
                            }];
}


-(void)handleNetworkError:error
{
     // Do some error handling
     [self startNetworkDownloadForObjectAtIndex:self.lastUnsentObjectIndex];
 }

-(void)startDownloadTimer
{
     if (self.syncEngineTimer) [self.syncEngineTimer invalidate];
     self.syncEngineTimer = [NSTimer scheduledTimerWithTimeInterval:kSyncTimeInterval
                                                        target:self
                                                    selector:@selector(restartNetworkDownload)
                                                      userInfo:nil
                                                       repeats:NO];
}

-(void)restartNetworkDownload
{
      // do some fancy calculations / etc to manage your download
     int anIndex = theResultOfYourCalculation;
     [self startNetworkDownloadForObjectAtIndex:anIndex];
}

OK, this is an example of a possible recursive call of several network downloads (get 100 flicker pictures for instance) and try to get the new ones after 1 hour, for instance. 好的,这是一个可能的递归调用几个网络下载的例子(例如获得100个闪烁的图片),并尝试在1小时后获得新的网络。 Excuse any coding typos. 请原谅任何编码错别字。

We're running this under ARC for iOS devices above iOS 5.0 我们在ARC下为iOS 5.0以上的iOS设备运行此功能

We obviously broke the first level of retain cycle by using a weakSelf reference pointer when using self.downloadManager keeping a reference to the success and failure completion block. 当使用self.downloadManager保持对成功和失败完成块的引用时,我们显然通过使用weakSelf引用指针打破了第一级保留周期。 This is all fine and goes well in instruments. 这一切都很好,在乐器中也很顺利。

Now, when looking at allocation in Instruments, we launch a download operation for several downloads. 现在,在查看Instruments中的分配时,我们会启动下载操作以进行多次下载。 Instruments shows no leaks. 仪器没有泄漏。 BUT when regurlarly saving the heap, you can see it slowly growing. 但是当重新保存堆时,你可以看到它慢慢增长。

Checking allocations and having a look at the call stack, it definitely looks like the block is keeping a reference to self through the use of startDownloadTimer 检查分配并查看调用堆栈,看起来块看起来像是通过使用startDownloadTimer来保持对self的引用

Any explanation on a possible cause and solution will be greatly appreciated :) 任何可能的原因和解决方案的解释将不胜感激:)

Your timer retains its target (the self ). 您的计时器保留其目标( self )。

Try the solution from this question: Weak Reference to NSTimer Target To Prevent Retain Cycle 尝试这个问题的解决方案: 弱参考NSTimer目标以防止保留周期

Or use dispatch_after instead of timer. 或者使用dispatch_after代替计时器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM