Ios NSPERSISTENTCONTAINE&;具有大数据集的NSFetchedResultsController
我们最近将应用程序切换到使用Ios NSPERSISTENTCONTAINE&;具有大数据集的NSFetchedResultsController,ios,nsfetchedresultscontroller,nspersistentcontainer,Ios,Nsfetchedresultscontroller,Nspersistentcontainer,我们最近将应用程序切换到使用nspersistentcainer来设置核心数据堆栈。删除样板文件(如自动使用保存通知和合并处理)对我们很有吸引力,而且它的设置应该非常高效 然而,我们在导入大型数据集时面临一个问题。首先,我要告诉您,我们的数据模型相当复杂,有很多一对多的关系和实体 用于设置核心数据堆栈以使用连接到NSPersistentStoreCoordinator的专用队列NSManagedObjectContext,在后台队列上执行持久化。然后,主队列上下文将是该上下文的子上下文;专用队列
nspersistentcainer
来设置核心数据堆栈。删除样板文件(如自动使用保存通知和合并处理)对我们很有吸引力,而且它的设置应该非常高效
然而,我们在导入大型数据集时面临一个问题。首先,我要告诉您,我们的数据模型相当复杂,有很多一对多的关系和实体
用于设置核心数据堆栈以使用连接到NSPersistentStoreCoordinator
的专用队列NSManagedObjectContext
,在后台队列上执行持久化。然后,主队列上下文将是该上下文的子上下文;专用队列上下文创建为主队列上下文的子级以处理保存。在NSPersistentContainer
发明之前建立的一个相当标准
然而,当我们开始注意到随着数据集的增大,对应用程序进行分析会显示核心数据在主线程上占用了大量CPU时间。切换到NSPersistentContainer
似乎可以解决这个问题。主线程上的活动要少得多。我们假定这是因为通过主队列的流量较少(因为nPersistentContainer
的后台队列由newBackgroundQueue()
提供,设置为直接保存到商店协调员;它们不是主队列上下文的子级)
在数据集增长之前,这一切似乎都很好。我们注意到,当处理大约15000条记录时(有时包含多达10-15000个与这些记录相关的对象),在保存后台上下文时,如果设置了NSFetchedResultsController
,以观察这些对象,则UI将挂起。非常最多1分钟。显然,这是不可取的
下面是如何设置持久化容器:
...
public init(storeURL: URL, modelName: String, configureStoreDescriptionHandler: ((NSPersistentStoreDescription, NSManagedObjectModel) -> ())? = nil) throws {
guard let modelURL = Bundle.main.url(forResource: modelName, withExtension: "momd") else { throw StackError.modelNotFound }
guard let model = NSManagedObjectModel(contentsOf: modelURL) else { throw StackError.modelNotCreated }
let storeDescription = NSPersistentStoreDescription(url: storeURL)
storeDescription.type = NSSQLiteStoreType
configureStoreDescriptionHandler?(storeDescription, model)
storeDescription.shouldMigrateStoreAutomatically = true
storeDescription.shouldInferMappingModelAutomatically = true
storeDescription.shouldAddStoreAsynchronously = false
container = NSPersistentContainer(name: modelName, managedObjectModel: model)
container.persistentStoreDescriptions = [storeDescription]
var outError: StackError?
container.loadPersistentStores { (storeDescription, error) in
if let error = error {
assertionFailure("Unable to load \(storeDescription) because \(error)")
outError = .storeNotMigrated
}
}
if let error = outError {
throw error
}
container.viewContext.automaticallyMergesChangesFromParent = true
}
public var mainQueueManagedObjectContext: NSManagedObjectContext {
return container.viewContext
}
public func newPrivateQueueContext() -> NSManagedObjectContext {
let context = container.newBackgroundContext()
return context
}
...
我们通过newPrivateQueueContext()
获取私有队列上下文,执行我们的工作,然后保存。大型数据集导致NSFetchedResultsController
挂起
Apple建议设置viewContext.automaticallyMergesChangesFromParent=true
,直接保存到持久存储比在父子配置中保存到中间人(视图上下文)更有效:
这两个上下文都连接到同一个persistentStoreCoordinator,它作为其父上下文用于数据合并。这比在父上下文和子上下文之间合并更有效
实际上,我们已经设法解决了这个问题,方法是删除automaticallyMergesChangesFromParent=true
,并对私有队列上下文的配置方式进行以下更改:
...
public var mainQueueManagedObjectContext: NSManagedObjectContext {
return container.viewContext
}
public func newPrivateQueueContext() -> NSManagedObjectContext {
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.parent = container.viewContext
NotificationCenter.default.addObserver(self, selector: #selector(handlePrivateQueueContextDidSaveNotification(_:)), name: .NSManagedObjectContextDidSave, object: context)
return context
}
@objc func handlePrivateQueueContextDidSaveNotification(_ note: Notification) {
container.viewContext.performAndWait {
try? container.viewContext.save()
}
}
...
事实上,这在父子配置中配置了我们的主上下文和子上下文——据苹果公司称,这种配置效率较低
这管用!数据已正确保存到磁盘(已验证),数据有效(已验证),并且不再有NSFetchedResultsController
挂起
然而,这提出了几个问题:
- 为什么苹果推荐的设置
的方法会导致在处理大型数据集时锁定主队列?它不是应该更有效率吗?我们有什么遗漏吗NSPersistentContainer
- 有没有人遇到过这样的问题,也许用不同的方式解决了?我们找不到关于设置
在线处理大型数据集的详细信息NSPersistentContainer
- 你能看到我们设置堆栈的方式有什么问题吗 也许建议对配置进行改进
- 看起来好像是直接保存到持久性存储,然后合并中的更改比父子配置效率低?有人能解释一下吗
我应该补充一点,我们试图通过设置
fetchBatchSize
和改进谓词来提高我们的NSFetchedResultsController
的效率,但没有效果。如果您找到了答案,请您解释一下您是如何克服这个问题的