我已经解决了几个星期的问题。每当我保存我的 Core Data 托管对象上下文时,它都会影响 UI 性能。我已经尽我所能,正在寻求一些帮助。
情况
我的应用程序使用两个NSManagedObjectContext
实例。一个属于应用程序委托,并附加了一个持久存储协调器。另一个是主 MOC 的子对象,属于一个Class
对象,称为PhotoFetcher
. 它使用NSPrivateQueueConcurrencyType
因此在此 MOC 上执行的所有操作都在后台队列中进行。
我们的应用程序从我们的 API 下载代表照片数据的 JSON 数据。为了从我们的 API 中检索数据,需要执行以下一系列步骤:
- 构造一个
NSURLRequest
对象,使用NSURLConnectionDataDelegate
协议构造请求返回的数据,或者处理错误。 - JSON 数据下载完成后,在辅助 MOC 的队列上执行以下操作:
NSJSONSerialization
使用基础类实例解析 JSON 。- 迭代解析的数据,根据需要在我的后台上下文中插入或更新实体。通常,这会产生大约 300 个新的或更新的实体。
- 保存背景上下文。这会将我的更改传播到主 MOC。
- 在主 MOC 上执行一个块以保存它的上下文。这是为了让我们的数据持久化到磁盘,一个
SQLite
存储。最后,对委托进行回调,通知他们响应已完全插入到核心数据存储中。
保存背景 MOC 的代码如下所示:
[AppDelegate.managedObjectContext performBlock:^{
[AppDelegate saveContext]; //A standard save: call to the main MOC
}];
当主对象上下文保存时,它还保存了自上次保存主对象上下文以来已下载的相当数量的 JPEG。目前,在 iPhone 4 上,我们以 70% 的压缩率下载 15 个 200x200 JPEG,或总共大约 2MB 的数据。
问题
这很有效,而且效果很好。我的问题是,一旦后台上下文保存,NSFetchedResultsController
在我的视图控制器中运行就会获取传播到主 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
无效。
谁能指出我的“秘诀”,它将为我提供后台托管对象上下文所承诺的 UI 性能?