2015-10-22 1 views
7

Я использую библиотеку выбора изображений, чтобы пользователь мог выбрать много изображений из своей фотобиблиотеки. Они возвращаются в виде массива PHAssets. Затем я хочу преобразовать все PHAssets в UIImages и записать их в хранилище приложения.Использование большого объема памяти через PHAssets и вызов requestImageForAsset

В настоящий момент я перебираю все активы и звоню requestImageForAsset синхронно. Моя проблема в том, что при запуске этого цикла наблюдается невероятно высокий уровень использования памяти (с 30 изображениями, шипами до 130 МБ). Я хотел бы предотвратить это.

Вот мой код:

for(PHAsset *asset in self.assets) { 
     NSLog(@"started requesting image %i", i); 
     [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:[self imageRequestOptions] resultHandler:^(UIImage *image, NSDictionary *info) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       assetCount++; 
       NSError *error = [info objectForKey:PHImageErrorKey]; 
       if (error) NSLog(@"Image request error: %@",error); 
       else { 
        NSString *imagePath = [appDelegate.docsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%i.png",i]]; 
        NSData *imageData = UIImagePNGRepresentation(image); 
        if(imageData) { 
         [imageData writeToFile:imagePath atomically:YES]; 
         [self.imagesArray addObject:imagePath]; 
        } 
        else { 
         NSLog(@"Couldn't write image data to file."); 
        } 
        [self checkAddComplete]; 
        NSLog(@"finished requesting image %i", i); 
       } 
      }); 
     }]; 
    i++; 
} 

Основываясь на бревнах, я вижу, что все «начиная запрашивающего изображение х» называют первым, а затем все блоки завершения («закончил запрашивающий изображение х»). Я думаю, что это может повлиять на проблему памяти. Вероятно, было бы меньше памяти, чтобы гарантировать, что блок завершения для каждой итерации вызывается до освобождения этих ресурсов и перехода к следующей итерации. Как я могу это сделать?

ответ

4

Для управления памятью используйте autoreleasepool.

for(PHAsset *asset in self.assets) { 
    // This autorelease pool seems good (a1) 
    @autoreleasepool { 
     NSLog(@"started requesting image %i", i); 
     [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:[self imageRequestOptions] resultHandler:^(UIImage *image, NSDictionary *info) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       //you can add autorelease pool here as well (a2) 
       @autoreleasepool { 
        assetCount++; 
        NSError *error = [info objectForKey:PHImageErrorKey]; 
        if (error) NSLog(@"Image request error: %@",error); 
        else { 
         NSString *imagePath = [appDelegate.docsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%i.png",i]]; 
         NSData *imageData = UIImagePNGRepresentation(image); 
         if(imageData) { 
          [imageData writeToFile:imagePath atomically:YES]; 
          [self.imagesArray addObject:imagePath]; 
         } 
         else { 
          NSLog(@"Couldn't write image data to file."); 
         } 
         [self checkAddComplete]; 
         NSLog(@"finished requesting image %i", i); 
        } 
       } //a2 ends here 
      }); 
     }]; 
     i++; 
    } // a1 ends here 
} 
+1

Спасибо.Я попробовал @autoreleasepool в цикле for, но положил его туда и там, и обработчик результата сделал трюк! – Charles

+1

Этот трюк не работает для меня. : -s –

5

@Inder Kumar Rathore трюк не работает для меня. Так я пытался узнать больше о PHImageManager here

Я обнаружил, что, если я перейти от

- requestImageForAsset:targetSize:contentMode:options:resultHandler:

в

- requestImageDataForAsset:options:resultHandler:

я буду получать изображение с той же размерности {5376 , 2688}, но размер в байте намного меньше. Поэтому проблема памяти решена.

надеюсь, что эта помощь !!

(примечание: [UIImage imageWithData: ImageData] использовать это, чтобы преобразовать NSData к UIImage)

0

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

Дан массив индексов выбранных фотографий в камеру ролл

  1. Запрос первый объект в массиве.
  2. В блоке завершения удалите первый объект в массиве и снова вызовите тот же метод.
  3. Повторять до массива пуст

Вот код.

- (void)processPhotos 
{ 
    NSIndexPath *indexPath = [_selectedPhotosArray objectAtIndex:0]; 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    PHAsset *asset = [_allPhotos objectAtIndex:indexPath.row]; 
    [self.imageManager requestImageForAsset:asset 
           targetSize:PHImageManagerMaximumSize 
           contentMode:PHImageContentModeAspectFill 
            options:self.imageRequestOptions 
           resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) { 
     NSError *error = [info objectForKey:PHImageErrorKey]; 
     if (_selectedPhotosArray.count > 0) { 
     [_selectedPhotosArray removeObjectAtIndex:0]; 
     } 
     if (error) { 
     NSLog(@"[CameraRoll] Image request error: %@",error); 
     } else { 
     if (result != nil) { 
      [self processImage:result]; 
     } 
     } 
     if (_selectedPhotosArray.count > 0) { 
     // Recurring loop 
     [self processPhotos]; 
     } 
    }]; 
    }); 
} 

Обязательно проверьте, не был ли массив незаполненным до вызова этого метода в первый раз.

+0

Используя ** @ autoreleasepool ** с этим, использование памяти может быть снижено даже больше. –