gan*_*ogo 16 core-data ios swift
我有一个NSFetchedResultsController,我试图在背景上下文更新我的数据.例如,这里我试图删除一个对象:
persistentContainer.performBackgroundTask { context in
let object = context.object(with: restaurant.objectID)
context.delete(object)
try? context.save()
}
Run Code Online (Sandbox Code Playgroud)
有两件事我不明白:
NSFetchedResultsController在后台内容保存回其父级时更新,但这不会发生.我是否需要在主线程上手动触发某些内容?显然有一些我没有得到的东西.任何人都能解释一下吗?
我知道我已经正确地实现了获取的结果控制器委托方法,因为如果我改变我的代码直接更新viewContext,一切都按预期工作.
Tob*_*obi 31
NSPersistentContainer的实例方法performBackgroundTask(_:)并且newBackgroundContext()记录很少.
无论你调用哪种方法,在任何一种情况下,(返回的)临时NSManagedObjectContext设置privateQueueConcurrencyType都与之NSPersistentStoreCoordinator直接相关,因此没有parent.
见文档:
调用此方法会导致持久容器创建并返回一个新的NSManagedObjectContext,并将concurrencyType设置为privateQueueConcurrencyType.此新上下文将直接与NSPersistentStoreCoordinator关联,并设置为自动使用NSManagedObjectContextDidSave广播.
...或自己确认:
persistentContainer.performBackgroundTask { (context) in
print(context.parent) // nil
print(context.persistentStoreCoordinator) // Optional(<NSPersistentStoreCoordinator: 0x...>)
}
let context = persistentContainer.newBackgroundContext()
print(context.parent) // nil
print(context.persistentStoreCoordinator) // Optional(<NSPersistentStoreCoordinator: 0x...>)
Run Code Online (Sandbox Code Playgroud)
由于缺少a parent,更改不会被提交到parent context例如,viewContext并且在viewContext未触及的情况下,连接NSFetchedResultsController将不会识别任何更改,因此不会更新或调用其delegate方法.相反,更改将被直接推送到persistent store coordinator之后保存到persistent store.
我希望,我能够帮助你,如果你需要进一步的帮助,我可以补充一下,如你所说,如何获得我想要的行为.(下面添加解决方案)
您可以通过使用NSManagedObjectContext具有父子关系的两个s 来实现您所描述的行为:
// Create new context for asynchronous execution with privateQueueConcurrencyType
let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
// Add your viewContext as parent, therefore changes are pushed to the viewContext, instead of the persistent store coordinator
let viewContext = persistentContainer.viewContext
backgroundContext.parent = viewContext
backgroundContext.perform {
// Do your work...
let object = backgroundContext.object(with: restaurant.objectID)
backgroundContext.delete(object)
// Propagate changes to the viewContext -> fetched results controller will be notified as a consequence
try? backgroundContext.save()
viewContext.performAndWait {
// Save viewContext on the main queue in order to store changes persistently
try? viewContext.save()
}
}
Run Code Online (Sandbox Code Playgroud)
但是,您也可以坚持使用performBackgroundTask(_:)或使用newBackgroundContext().但如前所述,在这种情况下,更改将直接保存到持久性存储中,并且viewContext默认情况下不会更新.为了改变传播下来的viewContext,这将导致NSFetchedResultsController通知,你必须设置viewContext.automaticallyMergesChangesFromParent到true:
// Set automaticallyMergesChangesFromParent to true
persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
persistentContainer.performBackgroundTask { context in
// Do your work...
let object = context.object(with: restaurant.objectID)
context.delete(object)
// Save changes to persistent store, update viewContext and notify fetched results controller
try? context.save()
}
Run Code Online (Sandbox Code Playgroud)
请注意,一次性添加10.000个对象等大量更改可能会让您NSFetchedResultsController发疯,因此会阻止main queue.
除非您将视图上下文设置为自动合并来自父级的更改,否则视图上下文不会更新。viewContext 已经设置为您从 NSPersistentContainer 收到的任何 backgroundContext 的子级。
尝试只添加这一行:
persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
Run Code Online (Sandbox Code Playgroud)
现在,viewContext 将在 backgroundContext 保存后更新,这将触发 NSFetchedResultsController 更新。
这在我的项目中非常有效。 \n 在函数 updateEnglishNewsListener(:) 中,这里参数数据位于任何对象中,我进一步将其转换为 json 格式以进行保存。
\n\nCore Data 使用线程(或序列化队列)限制来保护托管对象和托管对象上下文(请参阅 Core Data 编程指南)。这样做的结果是,上下文假定默认所有者是分配它的线程或队列\xe2\x80\x94,这是由调用其 init 方法的线程确定的。因此,您不应在一个线程上初始化上下文,然后将其传递给另一线程。
\n\n共有三种类型\n1。ConfinementConcurrencyType\n2. PrivateQueueConcurrencyType\n3. 主队列并发类型
\n\nMainQueueConcurrencyType 创建与主队列关联的上下文,非常适合与 NSFetchedResultsController 一起使用。
\n\n在 updateEnglishNewsListener(:) 函数中,参数数据是您的输入。(数据->您要更新的数据。)
\n\n private func updateEnglishNewsListener(data: [AnyObject] ){\n\n //Here is your data\n\n let privateAsyncMOC_En = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)\n // The context is associated with the main queue, and as such is tied into the application\xe2\x80\x99s event loop, but it is otherwise similar to a private queue-based context. You use this queue type for contexts linked to controllers and UI objects that are required to be used only on the main thread.\n\n privateAsyncMOC_En.parent = managedObjectContext\n privateAsyncMOC_En.perform{\n // The perform(_:) method returns immediately and the context executes the block methods on its own thread. Here it use background thread.\n\n let convetedJSonData = self.convertAnyobjectToJSON(anyObject: data as AnyObject)\n for (_ ,object) in convetedJSonData{\n self.checkIFNewsIdForEnglishAlreadyExists(newsId: object["news_id"].intValue, completion: { (count) in\n\n if count != 0{\n self.updateDataBaseOfEnglishNews(json: object, newsId: object["news_id"].intValue)\n }\n })\n }\n do {\n if privateAsyncMOC_En.hasChanges{\n\n try privateAsyncMOC_En.save()\n\n }\n if managedObjectContext.hasChanges{\n\n try managedObjectContext.save()\n\n }\n\n }catch {\n print(error)\n }\n }\n }\nRun Code Online (Sandbox Code Playgroud)\n\n检查coredata中是否已经存在数据,避免数据冗余。Coredata没有主键概念,因此我们依次检查coredata中是否已存在数据。当且仅当更新数据已存在于 coredata 中时,数据才会被更新。这里 checkIFNewsIdForEnglishAlreadyExists(:) 函数返回 0 或 value 。如果返回 0,则数据不会保存在数据库中,否则会保存。我正在使用完成句柄来了解新数据或旧数据。
\n\n private func checkIFNewsIdForEnglishAlreadyExists(newsId:Int,completion:(_ count:Int)->()){\n\n let fetchReq:NSFetchRequest<TestEntity> = TestEntity.fetchRequest()\n fetchReq.predicate = NSPredicate(format: "news_id = %d",newsId)\n fetchReq.fetchLimit = 1 // this gives one data at a time for checking coming data to saved data\n\n do {\n let count = try managedObjectContext.count(for: fetchReq)\n completion(count)\n\n }catch{\n let error = error as NSError\n print("\\(error)")\n completion(0)\n }\n\n\n }\nRun Code Online (Sandbox Code Playgroud)\n\n根据需要将旧数据替换为新数据。
\n\n private func updateDataBaseOfEnglishNews(json: JSON, newsId : Int){\n\n do {\n let fetchRequest:NSFetchRequest<TestEntity> = TestEntity.fetchRequest()\n\n fetchRequest.predicate = NSPredicate(format: "news_id = %d",newsId)\n\n\n let fetchResults = try managedObjectContext.fetch(fetchRequest as! NSFetchRequest<NSFetchRequestResult>) as? [TestEntity]\n if let fetchResults = fetchResults {\n\n if fetchResults.count != 0{\n let newManagedObject = fetchResults[0]\n newManagedObject.setValue(json["category_name"].stringValue, forKey: "category_name")\n newManagedObject.setValue(json["description"].stringValue, forKey: "description1")\n\n do {\n if ((newManagedObject.managedObjectContext?.hasChanges) != nil){\n\n try newManagedObject.managedObjectContext?.save()\n\n }\n\n } catch {\n let saveError = error as NSError\n print(saveError)\n }\n }\n\n }\n\n } catch {\n\n let saveError = error as NSError\n print(saveError)\n }\n }\nRun Code Online (Sandbox Code Playgroud)\n\n将任何对象转换为 JSON 以保存在 coredata 中
\n\n func convertAnyobjectToJSON(anyObject: AnyObject) -> JSON{\n let jsonData = try! JSONSerialization.data(withJSONObject: anyObject, options: JSONSerialization.WritingOptions.prettyPrinted)\n let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue)! as String\n if let dataFromString = jsonString.data(using: String.Encoding.utf8, allowLossyConversion: false) {\n let json = JSON(data: dataFromString)\n return json\n }\n return nil\n }\nRun Code Online (Sandbox Code Playgroud)\n\n希望它能帮助你。如果有任何困惑,请询问。
\n| 归档时间: |
|
| 查看次数: |
3230 次 |
| 最近记录: |