Search code examples
objective-ciosobjective-c-blocksalassetalassetslibrary

enumerateGroupsWithTypes won't execute blocks when polling in main thread


On iOS 4.x I am attempting to enumerate through all of the photos on the device and when that has completed then process that list further within the same method.

As the blocks of enumerateGroupsWithTypes run asynchronously on another thread and I can not see how to keep execution of the main thread from continuing, my solution to keeping the further processing from starting until ready is to poll the array of photos I am collecting until it sees that it has completed populating by the inclusion of an NSNull object at the end.

On iOS 4.0 it works fine - the polling continues as the other thread enumerates the photos and then execution continues on the main thread when that is done. On iOS 4.1+ the polling is some how stopping the other thread from executing any of it's blocks so the polling gets stuck in an infinite loop.

Is there a better way to achieve this other than embracing the asynchronous nature of the enumeration by breaking the further processing into a different method that the enumeration block can call?

Bonus points: why does my polling approach work on 4.0 but not 4.1+?

NSMutableArray *photos = [NSMutableArray new];

void (^assetEnumerator)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
    if(result != NULL) {
        [photos addObject:result];
    }
};
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
    if(group != nil)
        [group enumerateAssetsUsingBlock:assetEnumerator];
    else
        [photos addObject:[NSNull null]];
};

ALAssetsLibrary *library = [ALAssetsLibrary new];

[library enumerateGroupsWithTypes:ALAssetsGroupAll
                       usingBlock:assetGroupEnumerator
                     failureBlock:^(NSError *error) {
                         NSLog(@"%@", error);
                     }];

// keep polling until the photos have all been enumerated
// (NSNull is the last 'photo' added)
while (![photos count] || ![[photos objectAtIndex:[photos count]-1] isEqual:[NSNull null]]);

// ... further processing ...

Solution

  • The better way is to have your block kick off the further processing (e.g. using performSelectorOnMainThread:withObject:waitUntilDone:), rather than having your main thread poll for it.

    I can't say for sure why it works in 4.0 and fails in 4.1. It's possible that in 4.1 it is no longer asynchronous for some cases (e.g. when the user does not need to be prompted), or that in 4.1 it waits for a callback on the main thread before kicking off the background task, or that on your 4.1 device or simulator instance it is trying to ask for permission (and hanging up, as you have the main run loop blocked) and would fail on 4.0 too if you hadn't already authorized it to access the photos library in your earlier testing.