NSFetchedResultsController 中的 fetchBatchSize 和缓存被忽略

ali*_*ego 5 xcode core-data nsfetchedresultscontroller ios swift

我正在使用 NSFetchedResultsController 在聊天室应用程序中显示消息。

上下文变量在 appDelegate 中分配,并引用聊天室中使用的上下文。

let context = persistentContainer.viewContext
Run Code Online (Sandbox Code Playgroud)

我在 viewDidLoad 中初始化 NSFRC 如下:

func initializeResultsController() {
    let request = NSFetchRequest<Message>(entityName: "Message")
    let messageSort = NSSortDescriptor(key: "dateCreated", ascending: true)

    request.sortDescriptors = [messageSort]
    request.predicate = NSPredicate(format: "chatRoomId == %@", self.chatRoomId)
    request.fetchBatchSize = 30

    fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: "messageDateSectionIdentifier", cacheName: self.chatRoomId)
    fetchedResultsController.delegate = self

    do {
        try fetchedResultsController.performFetch()
    } catch {
        fatalError("Failed to initialize FetchedResultsController: \(error)")
    }
}
Run Code Online (Sandbox Code Playgroud)

sectionNameKeyPath ("messageDateSectionIdentifier") 是一个派生属性,因此可以将这些部分划分为日历日。

我有两个问题。首先,batchSize 似乎被忽略,其次缓存似乎对性能没有影响。消息越多,选择聊天室的延迟时间越长。1500 条消息约 1 秒。

当我编辑方案以在控制台中显示 coreData 信息时,当视图第一次出现时,多次执行 30 行的批处理请求,在一种情况下,数组大小为 1500。不确定这是错误数组还是填充数组。控制台打印输出是:

CoreData: annotation: sql connection fetch time: 0.0013s
CoreData: annotation: total fetch execution time: 0.0014s for 1454 rows.
CoreData: annotation: Bound intarray _Z_intarray0
CoreData: annotation: Bound intarray values.
Run Code Online (Sandbox Code Playgroud)

在此之后多次重复此操作,值为 30 行。

我尝试将 sectionNameKeyPath 简化为 dateCreated 以查看派生部分是否是问题,但根本没有区别。我还应该提到,与所有聊天应用程序一样,该应用程序在显示时最初会滚动到底部。

我想要的是让缓存工作并且让 fetchBatchSize 工作,以便最初从 coreData 中只提取 30 行,直到用户开始向上滚动。现在由这种方法引起的延迟对我的应用程序性能产生了可衡量的影响。

ali*_*ego 0

我终于在我的案例中找到了问题的原因。

如果您在 tableView heightForRowAt 中引用 fetchedResultsController,则 fetchBatchSize 将循环遍历并加载您指定的 fetchBatchSize 循环中的所有数据。

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let item = self.fetchedResultsController!.object(at: indexPath)

    // get and return height of item
    return item.heightOfItem
}
Run Code Online (Sandbox Code Playgroud)

如果您使用 UITableViewAutomaticDimension 或定义不需要引用 fetchedResultsController 的高度(即固定高度),那么您不会遇到此问题,并且 fetchBatchSize 将正常工作。

不幸的是,我发现 UITableViewAutomaticDimension 的滚动性能不可接受,所以我想我必须使用偏移量手动配置批量加载。

我还有一个导致数据循环加载的额外问题。在我的例子中,sectionNameKeyPath 是一个临时属性。这也引起了问题,但不幸的是这是必要的。

如果您在使用 NSFetchedResultsController 进行 fetchBatchSize 时遇到问题,我建议您查看这两个问题。