I am having some synchronization issue with loading asset from ALAssetsLibrary
.
Actually, what I am trying is to load some pictures from camera roll whose urls are given by some database query. Now after obtaining urls from database I use those urls to load the pictures using assetForURL
method of ALAssetsLibrary
and after the picture is loaded I display the picture to some view. So I call the method inside a loop that is executed every time the query result-set returns a record. And everything works fine till now. Below is a sample code to demonstrate the process:
ALAssetsLibrary* library = [ALAssetsLibrary new];
//dispatch_group_t queueGroup = dispatch_group_create();
while ([rs next]) {
//some data load up
//load thumbnails of available images
[library assetForURL:url resultBlock:^(ALAsset *asset) {
UIImage* img = [[UIImage imageWithCGImage:asset.aspectRatioThumbnail] retain];
//dispatch_group_async(queueGroup, dispatch_get_main_queue(), ^{
//create views and add to container view
CGFloat left = (8.0f + dimension) * i + 8.0f;
CGRect rect = CGRectMake(left, 8.0f, dimension, dimension);
TileView* tileView = [[NSBundle mainBundle] loadNibNamed:@"TileView" owner:nil options:nil][0];
[tileView setFrame:rect];
tileView.tag = i;
tileView.active = NO;
[self.thumbnailContainer addSubview:tileView];
//display image in tileView, etc.
//.............
if (img) {
[img release];
}
NSLog(@"block %d: %d",i,[self.thumbnailContainer.subviews count]);
//});
} failureBlock:^(NSError *error) {
NSLog(@"failed to load image");
}];
i++;
}
NSLog(@"outside block %d",[self.thumbnailContainer.subviews count]);
[library release];
In my code self.thumbnailContainer
is a UIScrollView and inside that I add my custom views to display the thumbnail images and it works as expected.
The real dilemma comes when I try to select the very last view added to self.thumbnailContainer
. I cant find any way to determine when all the asynchronous blocks of assetForURL
methods completed so that self.thumbnailContainer
actually contains some subviews. So if I log count of subviews of self.thumbnailContainer
just after the loop completes it shows 0. And after that I find all the block codes get executed increasing count of subviews. It is very expected behavior but contradicts my requirements. I have tried dispatch_group_
and dispatch_wait
methods from GCD but without any success.
Can anyone please suggest a workaround or an alternative coding pattern to overcome the situation. Any help would be highly appreciated. Thanks.
You might utilize a dispatch group, as you likely had in mind:
- (void) loadViewsWithCompletion:(completion_t)completionHandler {
ALAssetsLibrary* library = [ALAssetsLibrary new];
dispatch_group_t group = dispatch_group_create();
while ([rs next]) {
dispatch_group_enter(group);
[library assetForURL:url resultBlock:^(ALAsset *asset) {
UIImage* img = [[UIImage imageWithCGImage:asset.aspectRatioThumbnail] retain];
dispatch_async(dispatch_get_main_queue(), ^{
//create views and add to container view
...
dispatch_group_leave(group);
});
} failureBlock:^(NSError *error) {
NSLog(@"failed to load image");
dispatch_group_leave(group);
}];
i++;
}
[library release];
if (completionHandler) {
dispatch_group_notify(group, ^{
completionHandler(someResult);
});
}
... release dispatch group if not ARC
}
The code might have a potential issue though:
Since you asynchronously load images, they may be loaded all in parallel. This might consume a lot of system resources. If this is the case, which depends on the implementation of the asset loader method assetForURL:resultBlock:failureBlock:
, you need to serialize your loop.
Note: Method assetForURL:resultBlock:failureBlock:
may already ensure that access to the asset library is serialized. If the execution context of the completion block is also a serial queue, [UIImage imageWithCGImage:asset.aspectRatioThumbnail]
will be executed in serial. In this case, your loop just enqueues a number of tasks - but processes only one image at a time and you are safe.
Otherwise, if method assetForURL:resultBlock:failureBlock:
runs in parallel, and/or the block executes an a concurrent queue - images might be loaded and processed in parallel. This can be a bad thing if those images are large.