我已经解决了几个星期的一个问题。每当我保存 Core Data 托管对象上下文时,它都会导致 UI 性能中断。我已经尽我所能,正在寻求帮助。
情况
我的应用程序使用两个 NSManagedObjectContext
实例。一个属于应用程序委托(delegate),并附加了一个持久存储协调器。另一个是主MOC的子节点,属于Class
对象,称为 PhotoFetcher
.它使用 NSPrivateQueueConcurrencyType
所以在这个 MOC 上执行的所有操作都在后台队列中进行。
我们的应用程序从我们的 API 下载表示照片数据的 JSON 数据。为了从我们的 API 检索数据,需要执行以下步骤序列:
NSURLRequest
对象并使用 NSURLConnectionDataDelegate
协议(protocol)来构造从请求返回的数据,或处理错误。 NSJSONSerialization
解析 JSON进入 Foundation 类实例。 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 获得的最重的堆栈跟踪。
它似乎将更新保存到持久存储,但我不能确定。所以我删除了对
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
. dispatch_barrier
发生的获取。 .这些用于确保在到达障碍之前不会执行块。我在这里犹豫不决,但您可能想检查一下这是因为在内部,Core Data 正在保存在其他地方,因此延迟了任何其他获取请求的执行。再一次,这是一种疯狂且没有受过教育的猜测。但我会 试试 没有获取的结果 Controller 立即更新,看看你的口吃是否仍然发生。 对我来说突出的另一件事是你正在为一个 child 做很多工作
MOC
但是然后对父级执行保存。似乎大部分保存应该由 child 执行。但也可能是我有一段时间没有使用这部分 Core Data 了 :-)
关于objective-c - 后台托管对象上下文交错 UI 动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13110155/