我从ALAssetsLibrary
加载资产时遇到了一些同步问题。
实际上,我正在尝试的是从相机胶卷中加载一些图片,这些图片的网址是通过某些数据库查询给出的。现在从数据库中获取网址后,我使用这些网址使用assetForURL
ALAssetsLibrary
方法加载图片,并在图片加载后将图片显示到某个视图。所以我在每次查询结果集返回记录时执行的循环内调用该方法。到目前为止一切正常。以下是演示该过程的示例代码:
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];
在我的代码self.thumbnailContainer
中是一个UIScrollView,在里面我添加自定义视图以显示缩略图图像,它按预期工作。
当我尝试选择添加到self.thumbnailContainer
的最后一个视图时,真正的困境就出现了。我无法找到任何方法来确定何时assetForURL
方法的所有异步块都已完成,以便self.thumbnailContainer
实际包含一些子视图。因此,如果我在循环完成后记录self.thumbnailContainer
的子视图计数,则显示0.然后我发现所有的块代码都会被执行,从而增加了子视图的数量。这是非常期待的行为,但与我的要求相矛盾。我尝试过GCD中的dispatch_group_
和dispatch_wait
方法,但没有取得任何成功。
任何人都可以建议一种解决方法或替代编码模式来克服这种情况。任何帮助将受到高度赞赏。感谢。
答案 0 :(得分:4)
您可能会考虑使用调度组:
- (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
}
代码可能有潜在的问题:
由于您异步加载图像,因此可以并行加载它们。此可能会占用大量系统资源。如果是这种情况,这取决于资产加载器方法assetForURL:resultBlock:failureBlock:
的实现,则需要序列化您的循环。
注意:方法assetForURL:resultBlock:failureBlock:
可能已确保序列化对资产库的访问。如果完成块的执行上下文也是串行队列,则[UIImage imageWithCGImage:asset.aspectRatioThumbnail]
将以串行方式执行。在这种情况下,您的循环只会将许多任务排队 - 但一次只处理一个图像并且您是安全的。
否则,如果方法assetForURL:resultBlock:failureBlock:
并行运行,和/或块执行并发队列 - 可能会并行加载和处理映像。如果这些图像很大,这可能会是坏事。
答案 1 :(得分:1)
您可以通过两种方法解决此问题。他们是
1的 NSLock 即可。特别是 NSConditionLock 。基本上你创建一个条件为“挂起任务”的锁。然后在assetForURL的完成和错误块中,使用“全部完成”条件将其解锁。有了这个,在你调用assetForURL之后,只需使用你的“全部”标识符调用lockWhenCondition,你就完成了(它将等到块设置了这个条件)!
2.手动跟踪resultBlock
&中的计数failureBlock
注意:强>
我已经提到了一些关于ALAsset库in my answer的性能的重要事项。