Я столкнулся с большой проблемой. То, что я пытаюсь сделать, это получить данные с сервера, но сервер одним нажатием дает все данные, которые очень велики по количеству, и я выполняю весь свой процесс в основном потоке, поэтому в нем около 400-500 изображений. форма URL, которую я сохраняю в каталоге документов в виде NSData. Итак, в навигаторе dubug, когда потребление памяти достигло 80-90 МБ, мое приложение вылетело и показало следующую ошибку: -

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]; 
}
0
Abhishek Rajput 23 Янв 2015 в 15:00
 – 
iOSNoob
23 Янв 2015 в 15:03
Нет, я уже отключил своего зомби
 – 
Abhishek Rajput
23 Янв 2015 в 15:11
Сохранение данных в файловой системе в виде потока
 – 
Andrea
23 Янв 2015 в 15:37
Можете ли вы показать код для downloadInspectionDocuments
 – 
Paulw11
23 Янв 2015 в 15:42
Я не понимаю вашей точки зрения. Не могли бы вы уточнить
 – 
Abhishek Rajput
23 Янв 2015 в 15:42

2 ответа

ARC не является сборщиком мусора: он вставляет код управления памятью (сохраняет/освобождает) за вас, но вам все равно нужно убедиться, что вы не используете слишком много ресурсов (так же, как в коде без ARC).

Вы запускаете этот большой цикл в основном потоке, поэтому любая потребляемая память не будет освобождена до следующего цикла выполнения.

Вам нужно разбить эту функцию на более мелкие шаги, которые можно выполнять поэтапно.

На данный момент, если для одной итерации внешнего цикла функции не используется слишком много памяти, вы можете добавить пул автоматического освобождения на этом уровне (я вижу, что вы включили его во внутреннем цикле)

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

И он, по крайней мере, будет сливать то, что может, на каждой итерации.

Учитывая, что вы загружаете большое количество файлов и будете полагаться на сетевое подключение, я бы рекомендовал выполнять загрузку асинхронно. Если вы еще этого не сделали, ознакомьтесь с AFNetworking, чтобы упростить это. Это даст вам гораздо больший контроль над вашими ресурсами, чем вы получаете сейчас с ресурсоемким блокирующим вызовом в основном потоке.

0
davbryn 23 Янв 2015 в 16:00
Но я уже использовал пул автовыпуска, из которого я вызываю метод. так что это эквивалентно вашему предложению. Теперь я собираюсь сломать функцию, а затем посмотреть, что именно происходит.
 – 
Abhishek Rajput
23 Янв 2015 в 16:06

Вы можете сэкономить много работы, следуя советам Давбрина и Андреа по использованию 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];
}

Итак, все, что вам нужно сделать, это создать список вещей для загрузки, и в вашем блоке успеха начать загрузку другого файла.

0
Scott Grant 23 Янв 2015 в 16:51
Возможно, вы правы, но дело в том, что я уже завершил свое приложение. Поэтому я не хочу переделывать только из-за этой проблемы.
 – 
Abhishek Rajput
23 Янв 2015 в 17:06
1
Однако это серьезная проблема, достаточно серьезная, чтобы помешать полезности вашего приложения. Иногда трудно отказаться от пути, по которому вы уже шли, но может быть лучше развернуться и вернуться назад. Другой вариант — посмотреть, что делает AFNetwork для потоковой передачи в файл, и воспроизвести это самостоятельно.
 – 
Scott Grant
23 Янв 2015 в 17:07