Подтвердить что ты не робот

Сохранить большую партию фотографий с помощью новой фреймворка?

Я пытаюсь сохранить большую партию фотографий в библиотеке Photos, используя новый класс PHAssetChangeRequest в iOS 8. Проблема в том, что, похоже, демон, который сохраняет фотографии, сам неожиданно падает с умеренно большим числом фотографий (я пробую около 500). Кто-нибудь знает, как обойти это ограничение? Это проблема использования памяти в самом демоне? Это также может быть пределом тайм-аута для блока изменений, потому что между двумя приведенными ниже двумя заявлениями журнала есть незначительный пробел.

Должен ли демон assetsd уже учитывать этот прецедент, так как что-то вроде этого в значительной степени способно обрабатывать суперкомплексную модель и дизайн в новой структуре Photos? Сам образец документации демонстрирует возможность сохранения фотографии.

Вот пример моего кода:

[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    for (NSURL * url in fileURLs) {
        PHAssetChangeRequest * assetReq = [PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:url];
    }
    NSLog(@"Added %d assets",fileURLs.count);
} completionHandler:^(BOOL success, NSError *error) {
     if (!success){
         NSLog(@"%@",error);
     }
}];

И вот как выглядит мой вывод:

... Added 501 assets
... Connection to assetsd was interrupted or assetsd died
... Error Domain=NSCocoaErrorDomain Code=-1 "The operation couldn’t be completed. (Cocoa error -1.)

Я даже попробовал синхронный метод performChangesAndWait в PHPhotoLibrary, но он также имеет ту же проблему.

Я открыт для предложений/идей, застрял!: (

4b9b3361

Ответ 1

вместо creationRequestForAssetFromImageAtFileURL, я использовал этот метод и отлично работал для 10 изображений (эта часть кода повторяется в tableView:cellForRowAtIndexPath:)

    UIImage *thisImage=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@",serverInfo,self.URLs[indexPath.row]]]]];
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        PHAssetChangeRequest * assetReq = [PHAssetChangeRequest creationRequestForAssetFromImage:thisImage];
        NSLog(@"Added %ld assets",(long)indexPath.row);
    } completionHandler:^(BOOL success, NSError *error) {
        if (!success){
             NSLog(@"%@",error);
        }
}]; 

Ответ 2

Обработка большой партии изображений на устройстве, вы должны быть очень осторожны в управлении памятью даже в современные дни ARC. У меня было большое количество изображений для обработки (50+ с изменением размера), и в итоге я выбрал CGImageSourceCreateThumbnailAtIndex() как было предложено здесь, здесь. Он использует ImageIO, который должен быть очень эффективным. Единственная проблема, с которой я столкнулся, заключается в том, что по какой-то причине она все равно зависает в памяти, если я не закрою свой цикл for-loop в @autoreleasepool {}

for (ALAsset *asset in assets) {
        @autoreleasepool {
            resizedImage = [self thumbnailForAsset:asset maxPixelSize:JPEG_MAX_DIMENSION];
        // do stuff here
    }
}

Ответ 3

Идея делает это небольшими партиями. PHPhotoLibrary::performChanges подает запросы на изменение один раз все вместе, поэтому даже он смог закончить, вы не сможете получать какие-либо обновления о прогрессе от делегата или блока. На сессии WWDC 2017 года "Что нового в API-интерфейсах фотографий" появилось приложение примера " Создание больших библиотек для тестирования", что делает именно это. На каждом performChanges отправлено 10 изображений, а обновления пользовательского интерфейса - из блока завершения performChanges, причем Семафор блокирует поток до тех пор, пока не будет обработана одна партия. Я размещаю важные фрагменты кода здесь:

private func addPhotos() {
    let batchSize = min(photosToAdd, maxBatchSize)
    if batchSize <= 0 || !active {
        isAddingPhotos = false
        active = false
        return
    }

    workQueue.async {
        let fileURLs = self.generateImagesAndWriteToDisk(batchSize: batchSize)
        self.createPhotoLibraryAssets(with: fileURLs)

        DispatchQueue.main.async {
            self.addPhotos()
        }
    }
}

private func createPhotoLibraryAssets(with imageFileURLs: [URL]) {
    photoLibrarySemaphore.wait() // Wait for any ongoing photo library

    PHPhotoLibrary.shared().performChanges({
        let options = PHAssetResourceCreationOptions()
        options.shouldMoveFile = true
        for url in imageFileURLs {
            let creationRequest = PHAssetCreationRequest.forAsset()
            creationRequest.addResource(with: .photo, fileURL: url, options: options)
        }
    }) { (success, error) in
        if !success {
            print("Error saving asset to library:\(String(describing: error?.localizedDescription))")
        }
        self.photoLibrarySemaphore.signal()
    }
}

Выше generateImagesAndWriteToDisk - это метод, который вам нужно заменить тем, что когда-либо было вашим методом, чтобы вернуть пакет из 10 фото-адресов или так далее. Мне лично не нравится писать в рекурсии. Код можно легко изменить на стиль без рекурсии.

Ответ 4

Это не является решением для этого вопроса, однако это обходное решение. Вы все равно можете использовать старую ALAssetsLibrary для успешного сохранения файлов в приложении Camera Roll/Photos.

ALAssetsLibrary* lib = [[ALAssetsLibrary alloc] init];
[lib writeImageDataToSavedPhotosAlbum:imageData metadata:nil
                      completionBlock:^(NSURL *assetURL, NSError *error)
{
    // start saving your next image
}];

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

Затем вы можете преобразовать полученный в результате assetURL в PHAsset, если необходимо:

+ (PHFetchResult *)fetchAssetsWithALAssetURLs:(NSArray *)assetURLs
                                  options:(PHFetchOptions *)options