【问题标题】:UICollectionView locks main thread for ~10 seconds on performBatchUpdatesUICollectionView 在 performBatchUpdates 上锁定主线程约 10 秒
【发布时间】:2023-03-21 21:00:01
【问题描述】:

我有一个包含 300 个单元格的集合视图,由 NSFetchedResultsController 驱动。每隔一段时间,所有的对象都会更新,所以我会收到委托消息告诉我,我让集合视图处理更新,就像处理表格视图一样。不幸的是,每次发生这种情况时它都会将主线程锁定几秒钟......我不知道为什么。这是我的代码:

-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{

    [self.cascadeViewController.collectionView performBatchUpdates:^{

    NSLog(@"performingBatchUpdates");

    for (NSDictionary *change in self.changes) {

        [change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {

            NSFetchedResultsChangeType type = [key unsignedIntegerValue];

            if (type == NSFetchedResultsChangeInsert) {

                [self.cascadeViewController.collectionView insertItemsAtIndexPaths:@[obj]];

            } else if (type == NSFetchedResultsChangeDelete) {

                [self.cascadeViewController.collectionView deleteItemsAtIndexPaths:@[obj]];

            } else if (type == NSFetchedResultsChangeUpdate) {

                [self.cascadeViewController.collectionView reloadItemsAtIndexPaths:@[obj]];

            } else if (type == NSFetchedResultsChangeMove) {

                [self.cascadeViewController.collectionView moveItemAtIndexPath:obj[0] toIndexPath:obj[1]];

            }

        }];
    }

    NSLog(@"performingBatchUpdates end");

} completion:^(BOOL finished) {

    NSLog(@"completion");

    // TODO: implement
    //    [self configureMessageAndFooterView];

}];

NSLog(@"end of method");

[self.changes removeAllObjects];

}

这里发生了什么?在我的应用程序的实际执行中,一次更新所有 300 个对象不会经常发生,但足以让我担心它。我正在使用库存 UICollectionViewFlowLayout - 我需要做一些更自定义的事情吗?

【问题讨论】:

  • 你应该分析它。
  • 我有 - 它在内部 API 调用上做了很多事情。它命中 [UICollectionViewUpdate _computeGaps] 并且似乎对 NSArrays 和 indexPath 比较进行了很多排序。似乎没有调用任何我可以修改的委托方法,或者我可以替换的 UICollectionViewFlowLayout 方法。
  • 您在此期间解决了问题吗?我的 collectionview 中的 600 个项目也遇到了同样的问题,将主线程锁定了 20 秒!

标签: objective-c uicollectionview nsfetchedresultscontroller uicollectionviewlayout


【解决方案1】:

performBatchUpdates:completion: 在只有约 100 个元素的集合视图中将主线程锁定几秒钟时遇到了同样的问题。

在这个问题上花费了太多时间后,我找到了解决方案:确保单元格的大小(在 -collectionView:layout:sizeForItemAtIndexPath: 中返回或通过布局的 itemSize 属性定义)没有小数值强>。我通过在单元格的计算高度上应用floor 解决了我的性能问题。

话虽如此,我不知道为什么会发生这种情况。通过查看我们分析运行的堆栈跟踪,很多时间都花在了-[UIViewCollectionViewUpdate _computeGaps] 上,而-[NSArray sortedArrayUsingSelector:] 又调用了数百甚至数千次(以及CFSortIndexes__CFSimpleMergeSort...)。通过仅使用一个整数值作为我们单元格的高度,sortedArrayUsingSelector 被调用不到 10 次,整个过程在几分之一秒内完成。

【讨论】:

    【解决方案2】:

    我隐约记得以前看到过这样的行为,但我没有针对 NSFetchedResultsController + UICollectionViewFlowLayout 组合的解决方案,因为由于诸多问题,我们停止使用这两个类。您可以考虑查看我们开源的替代方案:

    1. TLIndexPathTools 作为NSFetchedResultsController 的替代品。它提供了一个与NSFetchedResultsController 非常相似的TLIndexPathController 类,除了它也适用于普通数组并且它可以进行动画排序和过滤(不像NSFetchedResultsController。有许多示例项目,包括一个Core Data
    2. VCollectionViewGridLayout 作为UICollectionViewFlowLayout 的替代品。它是一个统一的垂直滚动网格,因此它不如UICollectionViewFlowLayout 灵活,但在大多数情况下,它的动画效果通常要好得多,它具有粘性标题(如UITableView 标题)。有几个示例项目可让您在 UICollectionViewFlowLayoutVCollectionViewGridLayout 之间切换以查看改进。

    我们有一个 iPad 应用程序,它具有一个包含大约 1000 个项目的网格状集合视图,当我们的核心数据数据库在后台更新时,上述内容为我们提供了出色的性能和流畅的动画。

    【讨论】:

      猜你喜欢
      • 2017-02-03
      • 1970-01-01
      • 2012-09-15
      • 1970-01-01
      • 2012-01-23
      • 2014-08-21
      • 1970-01-01
      • 1970-01-01
      • 2012-09-21
      相关资源
      最近更新 更多