ALAssetsLibrary enumerateGroupsWithTypes: - Thread synchronization
I'm using [ALAssetsLibrary enumerateGroupsWithTypes:] to store the ALAssets in an array. As this is an asynchronous operation, I need to wait for it to finish before continuing my work.
I read Cocoa thread synchronisation when using [ALAssetsLibrary enumerateGroupsWithTypes:] and tried the recommended NSConditionLock. However, the blocks are always performed in the main thread, thus if I wait using the conditionl开发者_高级运维ock, the main thread is blocked and the blocks won't get executed -> I'm stuck. I even tried running the method loadAssets on a new thread, but still the blocks get executed on the main thread.
I can't find a way to actually wait for the enumeration to finish. Is there a way to force the blocks to a different thread than the main thread or anything else I can do?
Here's the code:
- (void)loadAssets
{
assets = [NSMutableArray array];
NSConditionLock *threadLock = [[NSConditionLock alloc] initWithCondition:THREADRUNNING];
void (^assetEnumerator)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop)
{
if(result != nil)
{
[assets addObject:result];
}
};
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop)
{
if(group != nil)
{
[group enumerateAssetsUsingBlock:assetEnumerator];
}
[threadLock lock];
[threadLock unlockWithCondition:THREADFINISHED];
};
void (^assetFailureBlock)(NSError *) = ^(NSError *error)
{
[threadLock lock];
[threadLock unlockWithCondition:THREADFINISHED];
};
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
[assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:assetGroupEnumerator failureBlock:assetFailureBlock];
[threadLock lockWhenCondition:THREADFINISHED];
[threadLock unlock];
[assetsLibrary release];
[threadLock release];
}
you are killing your assetsLibrary object before it finished to enumrate the objects. move it to your .h file and release it in dealloc.
I know this thread is probably dead, but I'm answering it because this is where I landed by Googling
The trick is to lock your background thread until the enumeration yields a nil-group. This category is one example of how to use it, you can use the category method as a drop-in replacement of the ALAssetsLibrary
enumeration method.
@implementation ALAssetsLibrary (EEEConcurrency)
- (NSUInteger)eee_enumerateGroupsLockedWithTypes:(ALAssetsGroupType)types
usingBlock:(ALAssetsLibraryGroupsEnumerationResultsBlock)enumerationBlock
failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock
{
NSAssert(![NSThread isMainThread], @"This would create a deadlock (main thread waiting for main thread to complete)");
enum
{
EEEAssetsLibraryDone,
EEEAssetsLibraryBusy
};
NSConditionLock *assetsLibraryConditionLock = [[NSConditionLock alloc] initWithCondition:EEEAssetsLibraryBusy];
__block NSUInteger numberOfGroups = 0;
[self enumerateGroupsWithTypes:types
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
enumerationBlock(group, stop);
if (group) numberOfGroups++;
if (!group || *stop)
{
[assetsLibraryConditionLock lock];
[assetsLibraryConditionLock unlockWithCondition:EEEAssetsLibraryDone];
}
}
failureBlock:^(NSError *error) {
failureBlock(error);
[assetsLibraryConditionLock lock];
[assetsLibraryConditionLock unlockWithCondition:EEEAssetsLibraryDone];
}];
[assetsLibraryConditionLock lockWhenCondition:EEEAssetsLibraryDone];
[assetsLibraryConditionLock unlock];
return numberOfGroups;
}
@end
Download the category at https://gist.github.com/epologee/8890692
This code chunk should work for you -->
ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
__block int numberOfGroups = 0;
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[assetsLib enumerateGroupsWithTypes:ALAssetsGroupAlbum usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if (group) {
numberOfGroups++;
}
else {
dispatch_semaphore_signal(sema);
}
} failureBlock:^(NSError *error) {
NSLog(@"enumerateGroupsWithTypes failure %@", [error localizedDescription]);
dispatch_semaphore_signal(sema);
}];
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
NSLog( @"number of groups is: %d", numGroups);
Although instead of DISPATCH_TIME_FOREVER you may want to make the time one second from now
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
_assetsLibrary = [[ALAssetsLibrary alloc] init];
ALAssetsLibraryGroupsEnumerationResultsBlock listGroupBlock = ^(ALAssetsGroup *group, BOOL *stop) {
if (group)
{
if (((NSString *)[group valueForProperty:ALAssetsGroupPropertyType]).intValue == ALAssetsGroupAlbum)
{
[albumSettingsList addObject:group];
} else {
dispatch_semaphore_signal(sema);
}
}
};
ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError *error) {
switch ([error code]) {
case ALAssetsLibraryAccessUserDeniedError:
case ALAssetsLibraryAccessGloballyDeniedError:
break;
default:
break;
}
dispatch_semaphore_signal(sema);
};
NSUInteger groupTypes = ALAssetsGroupAlbum | ALAssetsGroupEvent | ALAssetsGroupFaces | ALAssetsGroupSavedPhotos;
[self.assetsLibrary enumerateGroupsWithTypes:groupTypes usingBlock:listGroupBlock failureBlock:failureBlock];
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
whenever you want to get album list synchoronos with IOS 7
Use dispatch_semaphore_t to control it
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:^(ALAssetsGroup *assetsGroup, BOOL *stop) {
if (assetsGroup) {
// Filter the assets group
[assetsGroup setAssetsFilter:[ALAssetsFilter allAssets]];
// Add assets group
if (assetsGroup.numberOfAssets > 0) {
// Add assets group
DNAlbum *album = [DNAlbum albumWithAssetGroup:assetsGroup];
[albumArray addObject:album];
}
}
if (*stop || !assetsGroup) {
dispatch_semaphore_signal(sem);
}
} failureBlock:^(NSError *error) {
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
精彩评论