утечка памяти при запросе фотографий с помощью фреймворка Photos

Я использую следующий метод, чтобы запросить несколько фотографий и добавить их в массив для последующего использования:

-(void) fetchImages{

        self.assets = [[PHFetchResult alloc]init];
        PHFetchOptions *options = [[PHFetchOptions alloc] init];
        options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
        self.assets = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:options];

        self.photosToVideofy = [[NSMutableArray alloc]init];

        CGSize size = CGSizeMake(640, 480);
        PHImageRequestOptions *photoRequestOptions = [[PHImageRequestOptions alloc]init];
        photoRequestOptions.synchronous = YES;

        for (NSInteger i = self.firstPhotoIndex; i < self.lastPhotoIndex; i++)
        {
            PHAsset *asset = self.assets[i];
            [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFit options:photoRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {
                if (result) {
                    [self.photosToVideofy addObject:result];


                }
            }];
        }
        NSLog(@"There are %lu photos to Videofy", (unsigned long)self.photosToVideofy.count);

}

Это нормально работает, когда количество фотографий меньше 50. После этого память подскакивает до 150-160мб, я получаю сообщение Connection to assetsd was interrupted or assetsd died и приложение вылетает. Как я могу освободить ресурсы (PHFetchResult) из памяти после того, как получу те, которые мне нужны? (нужно ли это?) Я хотел бы иметь возможность добавить до 150 фотографий. Любые идеи? Спасибо


person Kaitis    schedule 05.11.2014    source источник


Ответы (4)


Ваш код внутри метода fetchImages нуждается в некотором рефакторинге, взгляните на это предложение:

-(void) fetchImages {

    PHFetchOptions *options = [[PHFetchOptions alloc] init];
    options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    PHFetchResult *assets = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:options];

    self.photosToVideofy = [[NSMutableArray alloc] init];

    CGSize size = CGSizeMake(640, 480);
    PHImageRequestOptions *photoRequestOptions = [[PHImageRequestOptions alloc] init];
    photoRequestOptions.synchronous = YES;

    for (NSInteger i = self.firstPhotoIndex; i < self.lastPhotoIndex; i++)
    {
        PHAsset *asset = assets[i];
        [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFit options:photoRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {
            if (result) {
                [self.photosToVideofy addObject:result];


            }
        }];
    }
    NSLog(@"There are %lu photos to Videofy", (unsigned long)self.photosToVideofy.count);
}

Но проблема в потреблении памяти. Проведем некоторые расчеты.

Одно изображение с использованием ARGB и 4 байта на пиксель:

640x480x4 = 1.2MB

И теперь вы хотите хранить в оперативной памяти 150 изображений, поэтому:

150x1.2MB = 180MB

Например, iPhone 4 с 512 МБ зависнет, если вы используете более 300 МБ, но может быть и меньше, если другие приложения также потребляют много оперативной памяти.

Я думаю, вам следует подумать о сохранении изображений в файлы, а не в ОЗУ.

person Artur Kucaj    schedule 05.11.2014
comment
Спасибо за ваш ответ. - person Kaitis; 05.11.2014
comment
вам следует подумать о сохранении изображений в файлы, а не в ОЗУ. Только эта фраза мне очень помогла. На самом деле, при запросе видео мы получаем URL-адрес видео, а не само видео; и проблем с памятью не бывает. Поэтому я буду делать то же самое для изображений: запрашивать изображения, а затем немедленно сохранять их на диск в каталоге кэшей и передавать URL-адрес тому, кому нужен доступ к изображениям. - person Frédéric Adda; 09.06.2020

Вы не должны помещать результаты из PHFetchResult в массив. Идея PHFetchResult состоит в том, чтобы указать на множество изображений из библиотеки фотографий, не сохраняя их все в ОЗУ (я не уверен, как именно это делается), просто используйте объект PHFetchResult как массив, и он обрабатывает проблемы с памятью для вас. Например, подключите collectionViewController напрямую к объекту PHFetchResult и используйте PHImageManager для запроса изображений только для видимых ячеек и т. д.

Из документации Apple: «Однако, в отличие от объекта NSArray, объект PHFetchResult динамически загружает свое содержимое из библиотеки фотографий по мере необходимости, обеспечивая оптимальную производительность даже при обработке большого количества результатов». https://developer.apple.com/library/ios/documentation/Photos/Reference/PHFetchResult_Class/

person MadeByDouglas    schedule 11.11.2015
comment
Я согласен с тем, что для извлечения фотографий вам не нужно использовать массив ресурсов, но когда приходит время действительно запрашивать изображения, вы должны их где-то хранить... - person Frédéric Adda; 09.06.2020

Это может быть преднамеренным (не могу сказать, не глядя на остальную часть вашего кода), но self.photosToVideofy никогда не выпускается: поскольку вы обращаетесь к нему в блоке, объект, которому вы передаете блок ([PHImageManager defaultManager] ) всегда будет иметь ссылку на массив.

Попробуйте явно очистить массив, когда закончите работу с изображениями. Сам массив по-прежнему не будет освобожден, но содержащиеся в нем объекты будут освобождены (или могут быть освобождены, если на них больше нигде нет ссылок).

Лучшее решение — удалить массив из блока. Но для этого потребуется изменить логику вашего кода.

person Anna Dickinson    schedule 05.11.2014
comment
Эй, Анна! Я наткнулся на ваш ответ. Я предполагаю, что это связано с сохранением/циклом? Я хотел спросить, актуально ли это в Swift? Если да, вызовет myArrayOfWhatEver = nil, надо очистить память? Спасибо! - person Roi Mulia; 23.04.2016

Вы должны установить

photoRequestOptions.synchronous = NO;

вместо

photoRequestOptions.synchronous = YES;

У меня работало, iOS 10.2

person pw2    schedule 09.02.2017
comment
Это не имеет ничего общего с тем, как хранятся запрошенные фотографии. - person Frédéric Adda; 09.06.2020