这是一个棘手的问题,答案可能对包括我在内的许多网络学徒有用。
关于上下文的一些背景信息:
- 假设您想从在线服务下载数据
- 您想异步执行此操作
- 您想一次下载一个,然后可能在完成前一个后再做一个。
一种巧妙的方法是使用递归。您可以提出的常见实现的问题是保留周期,在网络完成块和自我之间。这可以使用weakSelf 引用指针来解决。
但是,递归调用的保留周期呢?
我们已经实现了一个递归栈,自指向一个下载管理类,像这样:
-(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];
}
好的,这是一个可能递归调用多个网络下载的示例(例如,获取 100 张闪烁的图片)并尝试在 1 小时后获取新的图片。原谅任何编码错误。
我们在 ARC 下为 iOS 5.0 以上的 iOS 设备运行它
当使用 self.downloadManager 保持对成功和失败完成块的引用时,我们显然打破了第一级保留循环。这一切都很好,并且在乐器中运行良好。
现在,在查看 Instruments 中的分配时,我们会针对多个下载启动下载操作。仪器显示没有泄漏。但是当定期保存堆时,您可以看到它缓慢增长。
检查分配并查看调用堆栈,看起来该块通过使用 startDownloadTimer 保持对 self 的引用
任何可能的原因和解决方案的解释将不胜感激:)