ALAssetsLibrary, кажется, возвращает неправильное количество моих фотографий
Когда я использую ALAssetsLibrary для получения локальных фотографий, он работает нормально. Но после того, как я удалил несколько фотографий с помощью приложения "Фото", мое приложение зависло.
Информация о сбое:
"Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSOrderedSet enumerateObjectsAtIndexes:options:usingBlock:]: index 14 beyond bounds [0 .. 9]'".'14'
Похоже, что количество местных фотографий до сих пор не изменилось. И даже после того, как я выйду из своего приложения и перезапущу его снова, оно все еще падает.
Код доступа к локальной фотографии:
dispatch_async(dispatch_get_main_queue(), ^
{
@autoreleasepool
{
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(@"error occour =%@", [myerror localizedDescription]);
};
ALAssetsGroupEnumerationResultsBlock groupEnumerAtion = ^(ALAsset *result, NSUInteger index, BOOL *stop)
{
if (result!=NULL)
{
if ([[result valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypePhoto])
{
[self.g_imageArray addObject:result];
}
}
};
ALAssetsLibraryGroupsEnumerationResultsBlock
libraryGroupsEnumeration = ^(ALAssetsGroup* group, BOOL* stop)
{
if (group == nil)
{
return;
}
if (group!=nil) {
[group enumerateAssetsUsingBlock:groupEnumerAtion];
}
[self updatephotoList];
};
self.library = [[ALAssetsLibrary alloc] init];
[self.library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:libraryGroupsEnumeration
failureBlock:failureblock];
}
});
Если я сделаю еще одну фотографию с помощью системной камеры, мое приложение снова будет в порядке.
7 ответов
Похоже, это ошибка iOS, как вы сказали, что ALAssetsLibrary вернул неверное количество ваших фотографий, поэтому вы получили ошибку индексации за пределами границ. Обходной путь должен перезагрузить Вашу фотографию снова как это:
ALAssetsLibraryGroupsEnumerationResultsBlock
libraryGroupsEnumeration = ^(ALAssetsGroup* group, BOOL* stop)
{
if (group == nil)
{
return;
}
//Force to reload photo as numberOfAssets is broken
NSLog(@"how many picture I have in this group: %d",[group numberOfAssets]);
[group setAssetsFilter:[ALAssetsFilter allPhotos]];//this will cause group to reload
NSLog(@"how many picture I have in this group: %d",[group numberOfAssets]);
if (group!=nil) {
[group enumerateAssetsUsingBlock:groupEnumerAtion];
}
[self updatephotoList];
};
Регистрация наблюдателя не помогла в моем случае. Пользователи по-прежнему зависали, а я нет. До сегодняшнего дня.
Я выяснил, как решить эту аварию. В конце концов, это ошибка в библиотеке фотографий Apple, но есть обходной путь. Что вы делаете, вы устанавливаете фильтр на фото, а затем на видео, вместо того, чтобы оставить его в "активах" по умолчанию. Затем вы перечисляете его по одному разу для каждого и делаете некоторые хитрости, чтобы убедиться, что вы получаете окончательную точку "в ноль" для выполнения любых необходимых обновлений. Мой нынешний подход немного грязный, но вы поняли:
// pending is used to tell the block we're not done, even if result is NULL
BOOL pending = YES;
// resorted is just a flag I use in case there are no videos; if there are none, the block is never called at all, and thus the == NULL part never triggers
__block BOOL resorted = NO;
ALAssetsGroupEnumerationResultsBlock assetEnumerator = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
if(result != NULL) {
[assets addObject:result];
} else if (! pending) {
// ready!!
resorted = YES;
[self resort]; // my own method; replace with e.g. tableView reload!
}
};
// there are two types of assets - photos and videos; we start with photo
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
NSLog(@"assets = %d", group.numberOfAssets);
[group enumerateAssetsUsingBlock:assetEnumerator];
// we then set pending to NO; even though the enumeration happens in a separate thread, it seems like pending is not caught by the above enumeration (I have 105 images in the group I am enumerating, FWIW; it may be better to set pending in the == NULL part of the enumeration though
pending = NO;
// now we switch to video and do the same thing
[group setAssetsFilter:[ALAssetsFilter allVideos]];
BriefLog(@"assets = %d", group.numberOfAssets);
[group enumerateAssetsUsingBlock:assetEnumerator];
// if there are 0 vids, the above block is not ever called so we flip to a background thread and then back (probably not necessary) and then check if resorted is set; if it isn't we call resort
if (! resorted) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
if (! resorted) {
[self resort]; // my own method; replace with e.g. tableView reload!
}
});
});
Вот и все. Исключения NSRange исчезают. По крайней мере, в моем случае они сделали.
Я начал с ответа Qiulang, но это не сработало для меня. То, что сработало для меня, это вызвать setAssetsFilter 3 раза подряд со всеми комбинациями фильтров, прежде чем начинать перечисление.
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
[group setAssetsFilter:[ALAssetsFilter allVideos]];
[group setAssetsFilter:[ALAssetsFilter allAssets]];
Вам нужно зарегистрировать наблюдателя для ALAssetsLibraryChangedNotification
чтобы получить изменения в библиотеке. Когда уведомление сработает, повторно перечислите группы и содержимое AssetsLibrary. Если вы не зарегистрируетесь для уведомления, ваше приложение получит старый снимок библиотеки, и перечисление не будет выполнено. Также обратите внимание, что существует ошибка, связанная с ALAssetsLibraryChangedNotification
под iOS 5.x, как описано здесь: http://www.openradar.me/10484334
В iOS 8 я также заметил, что numberOfAssets возвращает неправильное количество фотографий, если некоторые из них были удалены и находятся в альбоме "Недавно удаленные" в настоящее время.
Библиотека ALAssetsLibrary исключена из библиотеки PHAssetsLibrary, поэтому используйте этот код:
__block PHAssetCollection *collection;
_arr_downloadedWallpaper = [[NSMutableArray alloc] init];
// Find the album
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"title = %@", @"Bhakti Ras"];
collection = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
subtype:PHAssetCollectionSubtypeAny
options:fetchOptions].firstObject;
PHFetchResult *collectionResult = [PHAsset fetchAssetsInAssetCollection:collection options:nil];
[collectionResult enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
//add assets to an array for later use in the uicollectionviewcell
NSLog(@"asset is =%@",asset);
if (asset) {
[self.arr_downloadedWallpaper addObject:asset];
}
}];
Для тех из вас, кто заполняет некоторый NSArray всеми ALAssetRepresentations и не хочет слишком сильно изменять свой код, просто отфильтруйте массив с помощью этого средства проверки - AssetURLChecker
Ура!