【问题标题】:fetchBatchSize and cache in NSFetchedResultsController ignoredNSFetchedResultsController 中的 fetchBatchSize 和缓存被忽略
【发布时间】:2018-06-02 07:18:06
【问题描述】:

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

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

let context = persistentContainer.viewContext

我在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)")
    }
}

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.

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

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

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

【问题讨论】:

    标签: ios swift xcode core-data nsfetchedresultscontroller


    【解决方案1】:

    batchSize 不被 fetchedResultsController 尊重是正确的。 NSFetchedResultsController 会进行一次获取,然后跟踪上下文中的所有更改,以查看是否添加、删除、移动或更改了任何内容。如果它仅通过尊重 batchSize 来获取匹配实体的子集,它将无法完成它的工作。

    您可以通过将谓词设置为仅在特定日期之后获取消息来解决此问题。为了弄清楚截止日期是什么,您可以先进行一次提取,其中batchSize = 1 和batchOffset = [您最初希望在fetchedResultsController 中有多少消息]。随着越来越多的消息进入集合,其大小将超过您的初始限制。

    还要注意,对于集合中的每个元素都会调用 sectionNameKeyPath。因此,即使在那里做少量的工作也会导致巨大的延误。不要在 sectionNameKeyPath 中创建日历或 dataFormatter - 重用一个。

    【讨论】:

    • 很高兴知道。我认为 nsfrc 可能只跟踪结果数组的变化。 Thx 还提供有关日历的建议。肯定会改变它以重用一个实例。
    • fetchedResultsController 跟踪与谓词匹配的所有更改。
    • 有道理。我会尝试你建议的谓词方法。谢谢。
    • 如果我想在@objc public var messageDateSectionIdentifier: String 的扩展中重用一个日历? {...最好在 appDelegate 中创建它并引用上面扩展中的 appDelegate 实例?
    • 我希望有一个核心样本,它显示了一个 fetchedResultsController 尊重 batchSize。我从来没有见过它。我看过很多有它的教程。我从未见过它影响行为。
    【解决方案2】:

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

    如果您在 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
    }
    

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

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

    我还遇到了另一个导致数据循环加载的问题。那就是 sectionNameKeyPath 在我的情况下是一个瞬态属性。这也引起了问题,但不幸的是这是必要的。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-24
      • 1970-01-01
      • 2011-06-18
      • 2012-08-24
      • 2016-10-23
      • 2018-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多