2

我剛剛更新到AFNetworking 2.0,我重新寫我的代碼來下載數據&其插入到核心數據。隊列AFHTTPRequestOperations創造記憶積累的

我下載JSON數據文件(從10-200mb文件的任何地方),並將其寫入磁盤,然後傳遞了他們對後臺線程來處理數據。下面是下載JSON &將代碼寫入磁盤的代碼。如果我讓它運行(甚至不處理數據),應用程序會使用內存直到它被殺死。

我認爲作爲數據進來,它被存儲在內存中,但一旦我保存到磁盤它爲什麼會留在記憶?不應該autorelease池照顧這個?我還設置了響應數據,並將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似乎減緩內存使用了一點,但它仍然積聚並最終崩潰的應用程序。

回答

3

如果你的JSON文件真的10-200mb每個,這肯定會引起內存問題,因爲這種要求是要加載在內存中的響應(而不是將它們分流到永久存儲)。更糟糕的是,因爲你使用JSON,我認爲問題是兩倍,因爲你將要加載到一個字典/數組,這也佔用了內存。因此,如果您有四個100MB的下載量,您的峯值內存使用量可能會達到800mb的數量級(NSData爲100mb,加上陣列/字典的可用空間爲100mb,可能要大得多),四個併發請求)。你可能會很快耗盡內存。

所以,一對夫婦的反應:

  1. 當這個數據量的處理,你想追求一個流媒體接口(NSURLConnection或者你寫的數據,它有NSURLSessionDataTask,而不是將其保存在內存中;或者使用NSURLSessionDownloadTask爲您做這些),將數據直接寫入永久存儲器(而不是在下載時將其保存在RAM中的NSData中)。

    如果您使用NSURLSessionDownloadTask,這非常簡單。如果您需要支持7.0之前的iOS版本,我不確定AFNetworking是否支持將響應直接傳輸到持久性存儲。我敢打賭你可以編寫自己的響應序列化程序,但是我沒有嘗試過。我總是編寫我自己的NSURLConnectionDataDelegate方法,直接下載到持久性存儲(例如like this)。

  2. 你可能不希望使用JSON這個(因爲NSJSONSerialization將加載整個資源到內存中,然後將其解析到一個NSArray/NSDictionary,也是在內存中),而是使用適合於流媒體格式解析響應(例如XML)並編寫一個解析器,該解析器在數據存儲(Core Data或SQLite)被解析時將數據存儲到數據存儲中,而不是嘗試將整個內容加載到RAM中。

    注意,甚至NSXMLParser是令人驚訝的存儲器效率低(見this question)。在XMLPerformance示例中,Apple演示瞭如何使用更繁瑣的LibXML2來最大限度地減少XML解析器的內存佔用量。順便說一句,我不知道你的JSON是否包含你編碼的任何二進制數據(例如base 64等),但如果是這樣的話,你可能想考慮一個二進制傳輸格式,它不會'不得不做這種轉換。使用base-64或uuencode或其他可以增加您的帶寬和內存要求。 (如果您不處理已編碼的二進制數據,則忽略此點。)

  3. 另外,您可能希望使用Reachability確認用戶的連接類型(Wifi vs蜂窩),因爲它被認爲是一種糟糕的形式,通過蜂窩下載大量數據(至少在沒有用戶許可的情況下),不僅僅是因爲速度問題,還包括使用過多的運營商月度數據計劃的風險。我甚至聽說蘋果歷史上拒絕試圖通過蜂窩下載太多數據的應用程序。

+0

感謝您的信息。在AFNetworking 2.0之前,我做了NSOperation的子類,並使用NSURLConnection來處理下載。 (雖然,當我完成下載時,我仍然將它保存到磁盤)。這樣做,我的記憶從來沒有超過50MB,這就是爲什麼我很困惑。我將嘗試使用AFNetworking實現NSURLSessionDownloadTask,以查看是否有幫助。謝謝! – RyanG

+1

我最終更新了舊的NSOperation子類,以便有效地工作。我沒有看到如此巨大的內存堆積。 – RyanG