Ios 阻止主线程的fetchedResultsController.object(at:indexPath)操作
我正在创建一个在集合视图中显示GIF的应用程序。 我从GIPHY公共API下载JSON,然后从每个单独的GIF URL获取数据,并使用Ios 阻止主线程的fetchedResultsController.object(at:indexPath)操作,ios,swift,core-data,Ios,Swift,Core Data,我正在创建一个在集合视图中显示GIF的应用程序。 我从GIPHY公共API下载JSON,然后从每个单独的GIF URL获取数据,并使用FLAnimatedImage将数据存储在UIImageView中,然后将其显示在集合视图中 我已经分叉了FLAnimatedImagepod,并添加了NSCoding协议,因此数据可以在核心数据中进行归档 在我的collectionView中,我正在使用fetchedResultsController获取填充我的collectionView的对象 由于分页,每当
FLAnimatedImage
将数据存储在UIImageView
中,然后将其显示在集合视图中
我已经分叉了FLAnimatedImage
pod
,并添加了NSCoding
协议,因此数据可以在核心数据中进行归档
在我的collectionView
中,我正在使用fetchedResultsController
获取填充我的collectionView
的对象
由于分页,每当我在cellForItemAt
中的collectionView
(单列单行)中获得一半图像时,我的API都会调用新图像
获取新图像的调用是异步的,当它从fetchRequest
返回结果时,我会更新集合视图。这不会阻塞主线程
现在的问题是:一旦我从下载任务中获得数据,我就将每个GIF保存为核心数据堆栈中的FLAnimatedImage
。现在,取消归档会导致为每个映像重新创建FLAnimatedImage,这会导致大量CPU开销,因为问题是创建FLAnimatedImage
的CPU非常繁重,因此将其从堆栈中取消归档。因此,这两个操作都是在分页时对新批图像执行的,最终导致我的应用程序崩溃
减速源于每次调用fetchedResultsController.object(at:indexPath)
时,我使用的是backgroundContext
,因此我知道我使用的不是线程。我认为造成崩溃的只是整个CPU开销。但是fetchedResultsController
不应该从堆栈中提取实体吗?为什么要在fetchedResultsController.object(at:indexPath)
调用中取消归档并创建属性类型(FLAnimatedImage)
是否有一种方法可以将FLAnimatedImage
存储在堆栈上,而不需要每次使用fetchedResultsController.object(at:indexPath)
将其从堆栈中取出时都重新创建
你能想出一个可能有用的策略吗
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GSGifCollectionViewCell", for: indexPath) as! GSGifCollectionViewCell
let gifObject:NSManagedObject = self.fetchedResultsController.object(at: indexPath)
gifObject.managedObjectContext?.perform {
let gsGifObject = gifObject as? GSGif
DispatchQueue.main.async {
cell.imageView.animatedImage = gsGifObject?.image
cell.rankLabel.text = "\(gsGifObject?.rank ?? 0)"
}
}
if let totalObjectsForSectionZero = fetchedResultsController.sections?[0].numberOfObjects {
self.checkCurrentIndexPathAndGetMoreGifsIfNeccessary(indexPath: indexPath, totalObjects:totalObjectsForSectionZero)
}
return cell
}
我有没有做错事
这是我的档案代码:
#import "FLAnimatedImage+NSCoding.h"
@implementation FLAnimatedImage (NSCoding)
static NSString *const kFLAnimatedImageCodingData = @"kFLAnimatedImageCodingData";
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
NSData *data = [aDecoder decodeObjectForKey:kFLAnimatedImageCodingData];
return [self initWithAnimatedGIFData:data];
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.data forKey:kFLAnimatedImageCodingData];
}
@end
这会导致主线程阻塞-[self-initWithAnimatedGIFData:data] 第一个-核心数据不是线程安全的-无论是用于读取还是写入。因此,替换
gifObject.managedObjectContext?.perform {
let gsGifObject = gifObject as? GSGif
DispatchQueue.main.async {
cell.imageView.animatedImage = gsGifObject?.image
cell.rankLabel.text = "\(gsGifObject?.rank ?? 0)"
}
}
简单地说:
let gsGifObject = gifObject as? GSGif
cell.imageView.animatedImage = gsGifObject?.image
cell.rankLabel.text = "\(gsGifObject?.rank ?? 0)"
您往返于后台线程没有任何帮助,并且违反了核心数据的多线程规则
其次,在核心数据中存储大量数据通常不是一个好主意,因为这在大多数关系数据库中都不是一个好主意。相反
要么:
a) 将图像存储在documents目录中,并仅将文件路径保存在core data中
b) 创建另一个仅包含数据blob的实体,并从GSGif创建与该实体的关系。这样,只有在访问关系时才会加载关系(在核心数据中称为“故障”)。最好将控制台的输出与您的问题一起发布。这将有助于诊断您的问题。您是否已将Xcode设置为在异常上设置断点?my cellForItemAt
中的这一行让gifObject:NSManagedObject=self.fetchedResultsController.object(at:indexPath)
在滚动时产生:Thread 1 EXEC\u BAD\u ACCESS(code=1,address:_)错误您提供的行的上下文很少,导致您的问题。我们可能应该查看整个cellForItemAt
方法EXEC\u BAD\u ACCESS
通常意味着您正在到达系统认为不应该到达的内存中的某个位置。如果我有线程问题,这种情况最常发生在我的计算机上。因此,你应该给我们一些关于这条线周围发生了什么的更多信息。作为旁注:请更新您的问题,不要在注释中添加代码,那里的格式很糟糕。我用整个cellForItemAt
方法更新了我的问题