ALAssetsLibrary — сбой после получения ALAssetsLibraryChangedNotification

В части моего приложения есть браузер фотографий, чем-то похожий на приложение Apple Photos, с начальным контроллером просмотра для просмотра миниатюр фотографий и подробным представлением, которое отображается при нажатии на фотографию.

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

Все работает отлично, пока я не получаю уведомление ALAssetsLibraryChangedNotification при переходе от одной фотографии к другой (в контроллере подробного представления), что часто приводит к сбою:

УВЕДОМЛЕНИЕ: библиотека ресурсов изменена // на мой собственный NSLog при появлении уведомления

загрузка ресурсов... // мой собственный NSLog, когда я начинаю перезагружать ресурсы в браузере эскизов

Ошибка утверждения: (размер == bytesRead), функция -[ALAssetRepresentation _imageData], файл /SourceCache/AssetsLibrary/MobileSlideShow-1373.58.1/Sources/ALAssetRepresentation.m, строка 224.

Конкретная строка кода, на которой происходит сбой, находится в вызове [метаданные currentRep], как показано здесь:

- (void)someMethod {
        NSURL *assetURL = [self.assetURLsArray objectAtIndex:index];
        ALAsset *currentAsset;

        [self.assetsLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) {

            [self performSelectorInBackground:@selector(configureDetailViewForAsset:) withObject:asset];

            } failureBlock:^(NSError *error) {
                    NSLog(@"failed to retrieve asset: %@", error);
        }];
}

- (void)configureDetailViewForAsset:(ALAsset *)currentAsset {
    ALAssetRepresentation *currentRep = [currentAsset defaultRepresentation];

    if (currentAsset != nil) {
        // do some stuff
    }
    else {
        NSLog(@"ERROR: currentAsset is nil");
    }

    NSDictionary *metaDictionary;
    if (currentRep != nil) {
        metaDictionary = [currentRep metadata];

        // do some other stuff
    }
    else {
        NSLog(@"ERROR: currentRep is nil");
    }
}

Я понимаю, что как только уведомление получено, оно делает недействительными любые ссылки на объекты ALAsset и ALAssetRepresentation... но как мне быть в ситуации, когда оно делает недействительным что-то прямо в середине попытки доступа к нему?

Я пытался установить BOOL прямо при получении уведомления, чтобы полностью прервать и предотвратить вызов [метаданные currentRep], но даже это не улавливает его каждый раз:

if (self.receivedLibraryChangeNotification) {
    NSLog(@"received library change notification, need to abort");
}
else {
    metaDictionary = [currentRep metadata];
}

Могу ли я что-нибудь сделать? На данный момент я почти готов отказаться от использования фреймворка ALAssetsLibrary.

(обратите внимание на эту нерешенную ветку на форумах разработчиков Apple, описывающую ту же проблему: https://devforums.apple.com/message/604430 )


person Jim Rhoades    schedule 17.05.2012    source источник


Ответы (1)


Кажется, проблема здесь:

[self.assetsLibrary assetForURL:nextURL 

    resultBlock:^(ALAsset *asset) {
        // You should do some stuff with asset at this scope
        ALAssetRepresentation *currentRep = [asset defaultRepresentation];
        // Assume we have a property for that
        self.assetRepresentationMetadata = [currentRep metadata];
        ...
        // assume we have a method for that
        [self updateAssetDetailsView];
    } 

    failureBlock:^(NSError *error) {
        NSLog(@"failed to retrieve asset: %@", error);
    }];

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

person voromax    schedule 26.05.2012
comment
Мне нужно провести дополнительные тесты... но, возможно, это помогло. Это не показано в моем коде выше (поскольку я хотел упростить его для размещения здесь), но [метаданные currentRep] вызывались в фоновом потоке. Я изменил это, чтобы вместо этого получить словарь метаданных внутри объекта assetsForURL resultBlock, как вы предложили, который находится в основном потоке. До сих пор он не разбился ... возможно, метод ALAssetRepresentation -metadata не является потокобезопасным? - person Jim Rhoades; 27.05.2012
comment
Самое главное здесь то, что для получения медиа с assetForURL требуется время. Поэтому за пределами блока завершения вы не можете быть уверены, что загрузили носитель... - person voromax; 27.05.2012
comment
Я обновил свой вопрос, чтобы точно показать, как я вызывал метод метаданных в фоновом потоке. Основываясь на вашем ответе, я изменил его, чтобы сначала получить словарь метаданных, а затем передать его и актив моему методу configureDetailView. Опять же, мне нужно сделать больше тестов, но пока все хорошо... - person Jim Rhoades; 27.05.2012