问题描述
我正在构建我的第一个iOS应用程序,这在理论上应该是相当简单,但我有困难使它足够的防弹我有信心提交到App Store。
I'm building my first iOS app, which in theory should be pretty straightforward but I'm having difficulty making it sufficiently bulletproof for me to feel confident submitting it to the App Store.
简单来说,主屏幕有一个表视图,选择一行后,它转换到另一个表视图,以主 - 详细方式显示与所选行相关的信息。基础数据每天从Web服务检索为JSON数据,然后缓存在Core Data存储中。将删除该日之前的数据以停止SQLite数据库文件无限增长。所有数据持久性操作都是使用Core Data执行的,并且有一个 NSFetchedResultsController
作为细节表视图的基础。
Briefly, the main screen has a table view, upon selecting a row it segues to another table view that displays information relevant for the selected row in a master-detail fashion. The underlying data is retrieved as JSON data from a web service once a day and then cached in a Core Data store. The data previous to that day is deleted to stop the SQLite database file from growing indefinitely. All data persistence operations are performed using Core Data, with an NSFetchedResultsController
underpinning the detail table view.
我看到的是,如果你在主屏幕和细节屏幕之间快速切换多次,而新的数据正在检索,解析和保存,应用程序冻结或崩溃完全。似乎有某种竞争条件,也许是由于Core Data在后台导入数据,而主线程试图执行提取,但我猜测。我遇到了捕获任何有意义的崩溃信息的麻烦,通常它是一个SIGSEGV深入的核心数据堆栈。
The problem I am seeing is that if you switch quickly between the master and detail screens several times whilst fresh data is being retrieved, parsed and saved, the app freezes or crashes completely. There seems to be some sort of race condition, maybe due to Core Data importing data in the background whilst the main thread is trying to perform a fetch, but I'm speculating. I've had trouble capturing any meaningful crash information, usually it's a SIGSEGV deep in the Core Data stack.
下表显示发生的事件的实际顺序细节表视图控制器已加载:
The table below shows the actual order of events that happen when the detail table view controller is loaded:
Main Thread Background Thread
viewDidLoad
Get JSON data (using AFNetworking)
Create child NSManagedObjectContext (MOC)
Parse JSON data
Insert managed objects in child MOC
Save child MOC
Post import completion notification
Receive import completion notification
Save parent MOC
Perform fetch and reload table view
Delete old managed objects in child MOC
Save child MOC
Post deletion completion notification
Receive deletion completion notification
Save parent MOC
当JSON数据到达时触发AFNetworking完成块后,将创建一个嵌套 NSManagedObjectContext
并传递给一个importer对象解析JSON数据并将对象保存到Core Data存储中。进口商使用iOS 5中引入的新的 performBlock
方法执行:
Once the AFNetworking completion block is triggered when the JSON data has arrived, a nested NSManagedObjectContext
is created and passed to an "importer" object that parses the JSON data and saves the objects to the Core Data store. The importer executes using the new performBlock
method introduced in iOS 5:
NSManagedObjectContext *child = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[child setParentContext:self.managedObjectContext];
[child performBlock:^{
// Create importer instance, passing it the child MOC...
}];
进口商对象观察自己的MOC NSManagedObjectContextDidSaveNotification
然后发布其自己的由细节表视图控制器观察到的通知。当发布此通知时,表视图控制器对其自身(父)MOC执行保存。
The importer object observes its own MOC's NSManagedObjectContextDidSaveNotification
and then posts its own notification which is observed by the detail table view controller. When this notification is posted the table view controller performs a save on its own (parent) MOC.
我使用与deleter对象相同的基本模式,用于在导入当天的新数据后删除旧数据。在获取的结果控制器获取新数据并且已重新载入详细信息表视图后,此操作是异步发生的。
I use the same basic pattern with a "deleter" object for deleting the old data after the new data for the day has been imported. This occurs asynchronously after the new data has been fetched by the fetched results controller and the detail table view has been reloaded.
我没有做的一件事是观察任何合并通知或锁定任何管理对象上下文或持久存储协调器。这是我应该做的吗?
One thing I am not doing is observing any merge notifications or locking any of the managed object contexts or the persistent store coordinator. Is this something I should be doing? I'm a bit unsure how to architect this all correctly so would appreciate any advice.
推荐答案
之前的iOS 5,我们' ve通常有两个 NSManagedObjectContexts
:一个用于主线程,一个用于后台线程。后台线程可以加载或删除数据,然后保存。然后将所得到的 NSManagedObjectContextDidSaveNotification
(正如你所做的)传递给主线程。我们调用 mergeChangesFromManagedObjectContextDidSaveNotification:
将那些进入主线程上下文。
Pre-iOS 5, we've usually had two NSManagedObjectContexts
: one for the main thread, one for a background thread. The background thread can load or delete data and then save. The resulting NSManagedObjectContextDidSaveNotification
was then passed (as you're doing) to the main thread. We called mergeChangesFromManagedObjectContextDidSaveNotification:
to bring those in to the main thread context. This has worked well for us.
这个的一个重要方面是 save:
在后台线程 mergeChangesFromManagedObjectContextDidSaveNotification:完成在主线程上运行(因为我们调用mergeChanges ...从监听器到该通知)之前,这确保主线程管理对象上下文看到这些更改。如果您有父子关系,但我们不知道您是否需要这样做,但是您在旧模式中做了以避免各种麻烦。
One important aspect of this is that the save:
on the background thread blocks until after the mergeChangesFromManagedObjectContextDidSaveNotification:
finishes running on the main thread (because we call mergeChanges... from the listener to that notification). This ensures that the main thread managed object context sees those changes. I don't know if you need to do this if you have a parent-child relationship, but you did in the old model to avoid various kinds of trouble.
我不知道在两个上下文之间有父子关系的优点是什么。从你的描述,似乎最后的保存到磁盘发生在主线程,这可能是不理想的性能原因。 (特别是如果您可能会删除大量数据;我们的应用程序中删除的主要费用总是在最终保存到磁盘期间发生。)
I'm not sure what the advantage of having a parent-child relationship between the two contexts is. It seems from your description that the final save to disk happens on the main thread, which probably isn't ideal for performance reasons. (Especially if you might be deleting a large amount of data; the major cost for deletion in our apps has always happened during final save to disk.)
您的代码是什么当控制器出现/消失,可能导致核心数据故障时运行?什么类型的堆栈跟踪是你看到崩溃?
What code are you running when the controllers appear/disappear that could be causing core data trouble? What kinds of stack traces are you seeing the crash on?
这篇关于同时可靠地使用核心数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!