Ios 阻止主线程的fetchedResultsController.object(at:indexPath)操作

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的对象 由于分页,每当

我正在创建一个在集合视图中显示GIF的应用程序。 我从GIPHY公共API下载JSON,然后从每个单独的GIF URL获取数据,并使用
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
方法更新了我的问题