iPad中产生的内存问题因为数据接收非常冗长

时间:2015-01-23 12:00:35

标签: ios objective-c ipad

我遇到了一个大问题。我想要做的是从服务器获取数据但是单击中的服务器给出了数量非常大的所有数据,我在主线程上执行所有的处理,因此有大约400-500个图像我以NSData的形式保存在文档目录中的URL形式。所以在dubug导航器中,当内存消耗达到80-90 mb左右时,我的应用程序崩溃并显示以下错误: -

mach_vm_map(size=135168) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
2015-01-23 17:10:03.946 ArchaioMobileViewer[853:148470] *** Terminating app due to uncaught exception 'NSMallocException', reason: 'Attempt to allocate 262144 bytes for NS/CFData failed'

我正在使用ARC,但我仍然遇到内存问题。这是我的代码` -

(void)downloadDocumentsFromServer:(NSDictionary *)documentsList IsUpdate:(BOOL)isUpdate;
{
    //Main Target(22)


BusinessLayer* bLL = [[BusinessLayer alloc]init];
FileManager* downloadImages = [FileManager alloc];

for(NSDictionary* inspDocumentResult in documentsList)
{

    FloorDocument* floorDocument = [[FloorDocument alloc]init];
    floorDocument.docID = [inspDocumentResult objectForKey:@"docID"];
    floorDocument.buildingID = selectedBuildingID;
    floorDocument.clientID = clientID;

    NSDictionary* documentArray = [inspDocumentResult objectForKey:@"Document"];

    floorDocument.docType = [[documentArray objectForKey:@"Type"] stringByTrimmingCharactersInSet:
                             [NSCharacterSet whitespaceAndNewlineCharacterSet]];
    floorDocument.docScale = [documentArray objectForKey:@"Scale"];
    floorDocument.docDescription = [documentArray objectForKey:@"DocDesc"];
    //floorDocument.floor = [bLL getFloorNameByDocIDAndBuildingID:selectedBuildingID DocID:floorDocument.docID];
    floorDocument.floor = [inspDocumentResult objectForKey:@"Floor"];

    NSLog(@"%@",[inspDocumentResult objectForKey:@"hiResImage"]);

    [downloadImages downloadInspectionDocuments:[inspDocumentResult objectForKey:@"hiResImage"] ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID];
    NSLog(@"Floor %@ - High Res Image copied for %@",floorDocument.floor,floorDocument.docID);

    //Download the Low Res Image
    NSString* lowResImage = [inspDocumentResult objectForKey:@"lowResImage"];

    [downloadImages downloadInspectionDocumentsLowRes:lowResImage ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID LowResName:@"lowResImage"];

    //Copy the Quarter Size File
    lowResImage = [lowResImage stringByReplacingOccurrencesOfString:@"LowRes" withString:@"LowRes4"];
    [downloadImages downloadInspectionDocumentsLowRes:lowResImage ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID LowResName:@"lowResImage4"];


    NSLog(@"Floor %@ - Low Res Images copied for %@",floorDocument.floor,floorDocument.docID);

    //Download the tiles
    NSArray* tiles = [inspDocumentResult objectForKey:@"lsUrls"];

    for(NSString* tile in tiles)
    {
        @autoreleasepool {
            NSArray* tileNameArray = [tile componentsSeparatedByString:@"/"];

            if(tileNameArray.count > 0)
            {
                NSString* destTile = [tileNameArray objectAtIndex:tileNameArray.count-1];
                destTile = [destTile stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@".%@",floorDocument.docType] withString:@""]; 

                NSLog(@"TileName:%@",destTile);
                [downloadImages downloadInspectionDocumentsTiles:tile ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID TileName:destTile];  
            }
        }


    }

    NSLog(@"Floor %@ - Tiles Image copied for %@",floorDocument.floor,floorDocument.docID);
    NSLog(@"Downloading Documents Tiles For %@ Completed at %@",floorDocument.docID,[bLL getCurrentDate]);
    [bLL saveFloorDocuments:floorDocument IsUpdate:isUpdate];
    // downloadImages=nil;


}

bLL = nil;

} 请帮我解决这个问题。

这是我在DownloadInspectionDocuments中使用的代码: -

    -(void)downloadInspectionDocuments:(NSString *)url ImageName:(NSString *)imageName FileType:(NSString*)fileType Folder:(NSString*)folder
{
    @autoreleasepool
  {
    NSString* source =[FileManager getInspectionDocumentsFolder];
    //Lets get the destination folder
    NSString *destination = [NSString stringWithFormat:@"%@/%@/%@",source,folder,imageName];

    [self createFolder:destination CreateSubFolders:true];

    NSString *filePath = [NSString stringWithFormat:@"%@/%@.%@",destination,imageName,fileType];
    NSFileManager* fm = [[NSFileManager alloc]init];

        if(![fm fileExistsAtPath:filePath])
        {
            NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
            [data1 writeToFile:filePath atomically:YES];
        }

    }

   // return [NSString stringWithFormat:@"%@.%@",imageName,fileType]; 
}

2 个答案:

答案 0 :(得分:0)

ARC不是垃圾收集:它会为你插入内存管理代码(保留/释放),但你仍然需要确保没有耗尽太多资源(就像你在非ARC中一样)码)。

您正在主线程上运行这个大循环,因此在下一个运行循环之前不会释放任何正在使用的内存。

您需要将此功能分解为可以分阶段执行的较小步骤。

现在,如果函数外循环的单次迭代没有消耗太多内存,你可以在该级别添加一个自动释放池(我看到你已经在内循环上)

for(NSDictionary* inspDocumentResult in documentsList)
{
 @autoreleasepool {
.... remaining code goes here
}
}

它至少会耗尽每次迭代的内容。

鉴于您正在下载大量文件并且将依赖于网络连接,我建议您异步执行下载。如果您还没有,请查看AFNetworking以简化此操作。这将使您对资源的控制比现在主线程上的资源密集型阻塞调用更多。

答案 1 :(得分:0)

通过遵循davbryn和Andrea的建议,您可以节省大量工作,以便使用AFNetworking并流式传输文件。基本上,不要将整个文件放在内存中,然后将其写入磁盘,在从网络获取字节时写入磁盘。这应该可以减轻内存压力。例如:

- (void)downloadFile:(NSString *)urlString {
  NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
  NSString *destinationPath = [NSDocumentDirectory() stringByAppendingPathComponent:@"some-file-name"];

  AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
  [operation setOutputStream:[NSOutputStream outputStreamToFileAtPath:destinationPath append:NO]]; 
  [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { 
    NSLog(@"Super duper awesome!");
    // Maybe start another download here?
  } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error downloading file: %@", error);
  }];

  [operation start];
}

因此,您需要做的就是生成要下载的内容列表,并在成功块中开始下载另一个文件。