我有一个线程问题,在收集视图中加载图像,其中数据来自cloudkit。我知道这是一个线程/阻塞问题,因为在我实现CK之前,我将一些图像转储到桌面上的文件夹中并从那里读取/解析它们并没有问题。使用CK,我只是通过仪表板创建了一些记录,并且我成功地获得了返回的预期记录,并使用这些结果中的图像填充CV单元格。我将CK查询结果存储在一个数组中,并使用该数组的大小来设置numberOfItemsInSection委托。
这里的问题是...在numberOfItemsInSection委托方法中,我调用了执行CK查询的模型类。由于这显然是一个网络调用,我把它放在后台线程中。从日志记录中,我可以看到查询执行,结果很快就会回来 - 在2-3秒内。但是,CV单元格从不显示,我也看不到自定义单元格已初始化(通过日志记录)。但是,如果我点击相机按钮并拍摄我已经实现的照片,我会将生成的图像添加到数组中,然后在CV上调用reloadData并显示所有单元格(和图像),包括用相机拍摄的新图像。
偶然地,我发现了一个有点工作的hack,它在CV 里面调用reloadData 在numberOfItemsInSection委托方法的后台线程中。结果,我以为我可能在调用reloadData时通过切换回主线程来偶然找到解决方案,但这使得它在一个无限循环中连续调用numberOfItemsInSection方法和cellForItemAtIndexPath并使其落到它滞后的地方到了一点,你几乎无法滚动和点击任何一个单元格都不会做任何事情。
此时,在尝试了许多不同的事情之后,我完全不知道如何解决这个问题。我知道这可能是一个非常简单的解决方案,因为异步加载图像以填充collectionview或tableview非常常见。有人可以提供一些指导吗?在此先感谢!!!
@property (nonatomic) NSInteger numberOfItemsInSection;
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
NSLog(@"***numberOfItemsInSection***");
dispatch_queue_t fetchQ = dispatch_queue_create("load image data", NULL);
dispatch_async(fetchQ, ^{
self.numberOfItemsInSection = [self.imageLoadManager.imageDataArray count];
[self.myCollectionView reloadData]; // should be done on main thread!
});
NSLog(@"numberOfItemsInSection: %ld", (long)self.numberOfItemsInSection);
return self.numberOfItemsInSection;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell"; // string value identifier for cell reuse
ImageViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
NSLog(@"cellForItemAtIndexPath: section:%ld row:%ld", (long)indexPath.section, (long)indexPath.row);
cell.layer.borderWidth = 1.0;
cell.layer.borderColor = [UIColor grayColor].CGColor;
cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
ImageData *imageData = [self.imageLoadManager imageDataForCell:indexPath.row]; // maps the model to the UI
dispatch_async(dispatch_get_main_queue(), ^{
if (imageData.imageURL.path) {
cell.imageView.image = [UIImage imageWithContentsOfFile:imageData.imageURL.path];
[cell setNeedsLayout];
} else {
// if imageURL is nil, then image is coming in from the camera as opposed to the cloud
cell.imageView.image = imageData.image;
[cell setNeedsLayout];
}
});
return cell;
}
答案 0 :(得分:0)
在返回self.numberOfItemsInSection之前,您应该等到异步调用完成。你可以使用信号量来做到这一点。但那你为什么要做这个异步?你只是得到一个数组的计数。然后你不应该在那里重新加载数据。你在哪里开始你的CloudKit查询?你在onViewDidLoad上做的吗?这也是异步操作。完成后,只需执行collectionView的reloadData。除此之外这样就足够了:
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [self.imageLoadManager.imageDataArray count];
}
如果你真的想在那里使用异步,那么你必须等待结果。您可以将代码更改为:
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
NSLog(@"***numberOfItemsInSection***");
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_queue_t fetchQ = dispatch_queue_create("load image data", NULL);
dispatch_async(fetchQ, ^{
self.numberOfItemsInSection = [self.imageLoadManager.imageDataArray count];
[self.myCollectionView reloadData]; // should be done on main thread!
dispatch_semaphore_signal(sema);
});
NSLog(@"numberOfItemsInSection: %ld", (long)self.numberOfItemsInSection);
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
return self.numberOfItemsInSection;
}
然后你为什么要去cellForItemAtIndexPath的主队列?它已经在主队列上执行了。