AFHTTPRequestOperations队列创build内存堆积

我刚刚更新到AFNetworking 2.0,我正在重写我的代码来下载数据并将其插入到Core Data中。

我下载JSON数据文件(从10到200MB的任何文件),将它们写入磁盘,然后将它们传递到后台线程来处理数据。 以下是下载JSON并将其写入磁盘的代码。 如果我只是让这个运行(甚至没有处理数据),应用程序使用内存,直到它被杀死。

我假设数据进入,它被存储在内存中,但是一旦我保存到磁盘,它为什么会留在内存中? 不应该autorelease池照顾这个? 我也设置了responseData和downloadData为零。 是否有明显的表明我在这里做错了?

@autoreleasepool { for(int i = 1; i <= totalPages; i++) { NSString *path = .... NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:path]]; AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request]; op.responseSerializer =[AFJSONResponseSerializer serializer]; [op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { //convert dictionary to data NSData *downloadData = [NSKeyedArchiver archivedDataWithRootObject:responseObject]; //save to disk NSError *saveError = nil; if (![fileManager fileExistsAtPath:targetPath isDirectory:false]) { [downloadData writeToFile:targetPath options:NSDataWritingAtomic error:&saveError]; if (saveError != nil) { NSLog(@"Download save failed! Error: %@", [saveError description]); } } responseObject = nil; downloadData = nil; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { DLog(@"Error: %@", error); }]; } [mutableOperations addObject:op]; } NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:mutableOperations progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) { DLog(@"%lu of %lu complete", (unsigned long)numberOfFinishedOperations, (unsigned long)totalNumberOfOperations); } completionBlock:^(NSArray *operations) { DLog(@"All operations in batch complete"); }]; mutableOperations = nil; [manager.operationQueue addOperations:operations waitUntilFinished:NO]; 

谢谢!

编辑 #1在我的完整块中添加一个@autoreleasepool似乎减缓了内存使用,但它仍然build立起来,并最终崩溃的应用程序。

如果你的JSON文件真的是每个10-200MB,这肯定会导致内存问题,因为这种请求会将响应加载到内存中(而不是将它们stream式传输到持久性存储)。 更糟糕的是,因为使用JSON,我认为问题是两倍,因为你将要加载到一个字典/数组,这也占用内存。 所以,如果你有四个100MB的下载量,你的峰值内存使用量可能是800MB的数量级( NSData 100mb加上arrays/字典的100mb加上可能要大得多),四个并发请求)。 你可能会很快耗尽内存。

所以,一些反应:

  1. 在处理这个数据量的时候,你需要一个stream接口(一个NSURLConnection或者NSURLSessionDataTask ,在你写入数据的时候写入数据,而不是保存在内存中;或者使用NSURLSessionDownloadTask来完成这个工作)将数据直接写入永久存储器(而不是在下载时将其保存在RAM中的NSData中)。

    如果您使用NSURLSessionDownloadTask ,这非常简单。 如果您需要支持7.0以前的iOS版本,我不确定AFNetworking是否支持将响应直接传输到持久性存储。 我敢打赌,你可以写你自己的响应序列化器,这样做,但我没有尝试过。 我一直写我自己的NSURLConnectionDataDelegate方法直接下载到持久性存储(例如这样的东西)。

  2. 你可能不希望为此使用JSON(因为NSJSONSerialization会将整个资源加载到内存中,然后将其parsing到NSArray / NSDictionary ,也就是在内存中),而是使用一种适用于stream式parsing响应的格式例如XML),并编写一个parsing器,将数据存储到您的数据存储(Core Data或SQLite),因为它正在被parsing,而不是尝试将所有东西加载到RAM中。

    请注意,即使是NSXMLParser出乎意料的内存效率低下(请参阅此问题 )。 在XMLPerformance示例中,Apple展示了如何使用更繁琐的LibXML2来最小化XMLparsing器的内存占用。

  3. 顺便说一下,我不知道你的JSON是否包含你编码的任何二进制数据(例如base64等),但如果是这样的话,你可能要考虑一个二进制传输格式,不必这样做转换。 使用base-64或uuencode或任何可以增加您的带宽和内存要求。 (如果你不处理已编码的二进制数据,则忽略这一点。)

  4. 顺便说一句,您可能想要使用Reachability来确认用户的连接types(Wifi vs蜂窝),因为通过蜂窝下载这么多的数据被认为是不好的forms(至less在没有用户许可的情况下),不仅仅是因为速度问题,而且会占用运营商月度数据计划的过多部分。 我甚至听说苹果历史上拒绝试图通过手机下载太多数据的应用程序。