objective-c - 后台托管对象上下文交错 UI 动画

标签 objective-c ios core-data user-interface nsmanagedobjectcontext

我已经解决了几个星期的一个问题。每当我保存 Core Data 托管对象上下文时,它都会导致 UI 性能中断。我已经尽我所能,正在寻求帮助。

情况

我的应用程序使用两个 NSManagedObjectContext实例。一个属于应用程序委托(delegate),并附加了一个持久存储协调器。另一个是主MOC的子节点,属于Class对象,称为 PhotoFetcher .它使用 NSPrivateQueueConcurrencyType所以在这个 MOC 上执行的所有操作都在后台队列中进行。

我们的应用程序从我们的 API 下载表示照片数据的 JSON 数据。为了从我们的 API 检索数据,需要执行以下步骤序列:

  • 构造一个 NSURLRequest对象并使用 NSURLConnectionDataDelegate协议(protocol)来构造从请求返回的数据,或处理错误。
  • JSON 数据下载完成后,在辅助 MOC 的队列上执行以下操作:
  • 使用 NSJSONSerialization 解析 JSON进入 Foundation 类实例。
  • 迭代解析的数据,根据需要在我的后台上下文中插入或更新实体。通常,这会产生大约 300 个新的或更新的实体。
  • 保存背景上下文。这会将我的更改传播到主 MOC。
  • 在主 MOC 上执行一个块以保存它的上下文。这是为了让我们的数据持久化到磁盘,一个 SQLite店铺。最后,对委托(delegate)进行回调,通知他们响应已完全插入到核心数据存储中。

  • 保存后台 MOC 的代码如下所示:
    [AppDelegate.managedObjectContext performBlock:^{
        [AppDelegate saveContext]; //A standard save: call to the main MOC
    }];
    

    当主对象上下文保存时,它还保存了自上次主对象上下文保存发生以来已下载的相当数量的 JPEG。目前,在 iPhone 4 上,我们正在以 70% 的压缩率下载 15 个 200x200 JPEG,或者总共大约 2MB 的数据。

    问题

    这很有效,而且效果很好。我的问题是,一旦后台上下文保存,NSFetchedResultsController在我的 View Controller 中运行会获取传播到主 MOC 的更改。它在我们的 PSTCollectionView 中插入新单元格,UICollectionView 的开源克隆.在插入新单元格时,主上下文会保存这些更改并将这些更改写入磁盘。在运行 iOS 5.1 的 iPhone 4 上,这可能需要 250-350 毫秒。

    在这三分之一秒内,该应用程序完全没有响应。保存之前正在进行的动画会暂停,并且在保存完成之前不会将新的用户事件发送到主运行循环。

    我使用 Time Profiler 在 Instruments 中运行我们的应用程序,以确定是什么阻塞了我们的主线程。不幸的是,结果相当不透明。这是我从 Instruments 获得的最重的堆栈跟踪。

    Instruments Heaviest Stack Trace

    它似乎将更新保存到持久存储,但我不能确定。所以我删除了对 saveContext 的任何调用因此 MOC 不会接触磁盘,并且主线程上的阻塞调用仍然存在。

    文本形式的跟踪如下所示:
    Symbol Name
    -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:]
     -[NSManagedObjectContext executeFetchRequest:error:]
      -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]
       _perform
        _dispatch_barrier_sync_f_invoke
         _dispatch_client_callout
          __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke_0
           -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:]
            -[NSManagedObjectContext executeFetchRequest:error:]
             -[NSPersistentStoreCoordinator executeRequest:withContext:error:]
              -[NSSQLCore executeRequest:withContext:error:]
               -[NSSQLCore objectsForFetchRequest:inContext:]
                -[NSSQLCore newRowsForFetchPlan:]
                 -[NSSQLCore _newRowsForFetchPlan:selectedBy:withArgument:]
                  -[NSSQLiteConnection execute]
    

    我试过的

    在我们接触 Core Data 代码之前,我们做的第一件事就是优化我们的 JPEG。我们切换到较小的 JPEG 并看到了性能提升。然后,我们减少了一次下载的 JPEG 数量(从 90 个减少到 15 个)。这也导致显着的性能提升。但是,我们仍然在主线程上看到 250-350 毫秒长的块。

    我尝试的第一件事就是摆脱背景 MOC 以消除它可能导致问题的可能性。事实上,这让事情变得更糟,因为我们的更新或创建代码在主线程上运行并导致整体动画性能下降。

    将持久存储更改为 NSInMemoryStoreType没有效果。

    任何人都可以向我指出“秘诀”,它会给我后台管理对象上下文所 promise 的 UI 性能吗?

    最佳答案

    我有一些猜测,按照您检查它们的顺序:

  • 它看起来很像在保存数据后发生的任何缓慢的事情(即,它在回调中)。这导致我要么重新加载 PTSCollectionView或重新获取获取的结果 Controller 。
  • UICollectionView会出现这个问题吗?在 iOS 6.x 上?如果没有,那将导致我依赖 PTSCollectionView .
  • 如果它仍然发生,那么这意味着它可能不是集合 View ,而是获取的结果 Controller 。看起来,从堆栈帧(尽管它们可能是不透明的)来看,获取的结果 Controller 正试图执行通过 dispatch_barrier 发生的获取。 .这些用于确保在到达障碍之前不会执行块。我在这里犹豫不决,但您可能想检查一下这是因为在内部,Core Data 正在保存在其他地方,因此延迟了任何其他获取请求的执行。再一次,这是一种疯狂且没有受过教育的猜测。但我会 试试 没有获取的结果 Controller 立即更新,看看你的口吃是否仍然发生。

  • 对我来说突出的另一件事是你正在为一个 child 做很多工作MOC但是然后对父级执行保存。似乎大部分保存应该由 child 执行。但也可能是我有一段时间没有使用这部分 Core Data 了 :-)

    关于objective-c - 后台托管对象上下文交错 UI 动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13110155/

    相关文章:

    iphone - AFImageRequestOperation 返回未完全下载的图像

    ios - SwiftUI:如何获取设备的宽度?

    ios - 来自 swift 前端的 POST 请求问题

    ios - 检查 coredata 对象是否为 nil

    ios - iCloud、Core Data 和来自 iOS 7 中网络服务的数据

    ios - UIImageView 意外改变大小

    ios - 是否可以在 Iphone 应用程序中下载 flickr 应用程序视频?如果是,那么如何?

    python - 在 OS X 上编写一个全屏不可关闭的窗口

    ios - 如何将 ImageView 及其属性从一个 View Controller 传递到另一个 View Controller ?

    iphone - 核心数据保存更改撤消