我正在创建一个在集合视图中显示gif的应用。
我从GIPHY公共API下载JSON,然后从每个单独的GIF URL获取数据,并使用FLAnimatedImage
将数据存储在UIImageView
中,然后在集合视图中显示它们。 / p>
我已经分配了FLAnimatedImage
pod
,并添加了NSCoding
协议,因此数据可以在Core Data中存档。
在我的collectionView
我使用fetchedResultsController
获取填充collectionView
的对象。
由于分页,每次我在collectionView
cellForItemAt
(单列单行)中的图像中途停止时,我的API都会调用新图像。
获取新图像的调用是异步的,只要它返回fetchRequest
的结果,我就会更新集合视图。这不会阻止主线程。
现在问题在于:我将下载任务中的数据作为核心数据堆栈中的FLAnimatedImage
保存后保存每个GIF。现在取消归档它会导致为每个映像创建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];
答案 0 :(得分:0)
首先 - 核心数据不是线程安全的 - 既不用于读取也不用于写入。所以替换
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)将图像存储在文档目录中,并仅保存核心数据中的文件路径。
b)创建另一个只有数据blob的实体,并从GSGif与它建立关系。这样,只有在访问它时才会加载关系(在核心数据中说“故障”)。