Open fatbobman opened 2 years ago
赞美博主,你的 CoreData 和 CloudKit 的系列文章帮我厘清了思路。每当遇到百思不得其解的问题时,来你的博客查找一番总会有很多收获,感恩 🙏
十分高兴能够对你有所帮助。🍺
2022年1月21日 下午12:56,Chris Akring @.***> 写道:
赞美博主,你的 CoreData 和 CloudKit 的系列文章帮我厘清了思路。每当遇到百思不得其解的问题时,来你的博客查找一番总会有很多收获,感恩 🙏
— Reply to this email directly, view it on GitHub https://github.com/fatbobman/blogComments/issues/125#issuecomment-1018180296, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANIYIGKVVBKC7BV775JTR2DUXDROXANCNFSM5HMU7A5Q. You are receiving this because you authored the thread.
最后一个例子中,context 中修改了 name 和 age,NSBatchUpdate 修改了 age 和 sex,采取策略 NSMergeByPropertyStoreTrumpMergePolicy。 我的理解是,这里因为采取逐属性比对,冲突的属性为 age,store 胜出,结果应该是 name 采用 context 中的指,age 和 sex 采用 store 中的值。 不知道我理解是否有误。
上下文数据在保存时,会发现它的快照版本已经与持久化中的数据版本不一样。按照NSMergeByPropertyStoreTrumpMergePolicy 规则进行比对。因为 上下文仅改动了两个属性 name 和 sex ,因此对于上下文来说,仅有这两个属性会出现冲突的情况,且一旦出现冲突将采用上下文版本的属性内容。 而,如果设置成 NSOverwriteMergePolicy , 则 三个属性都将使用上下文版本的属性内容。
逐个比对的意思是并非哪个数据版本新就使用那个,而是说是否保留没有发生冲突的属性
@fatbobman 上下文数据在保存时,会发现它的快照版本已经与持久化中的数据版本不一样。按照NSMergeByPropertyStoreTrumpMergePolicy 规则进行比对。因为 上下文仅改动了两个属性 name 和 sex ,因此对于上下文来说,仅有这两个属性会出现冲突的情况,且一旦出现冲突将采用上下文版本的属性内容。 而,如果设置成 NSOverwriteMergePolicy , 则 三个属性都将使用上下文版本的属性内容。
逐个比对的意思是并非哪个数据版本新就使用那个,而是说是否保留没有发生冲突的属性
我做了如下实验。在 CoreData Stack 的设置中:
container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
container.viewContext.automaticallyMergesChangesFromParent = false
# 这么设置后,background context 的数据持久化之后,viewContext 不会自动 merge,此时 viewContext 再次 save 时,则会产生冲突
Person(name="Xi", age=10, sex="male")
,保存,并存放在 people 中@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Person.name, ascending: true)],
animation: .default)
private var people: FetchedResults<Person>
...
private func addPerson() {
withAnimation {
let newPerson = Person(context: viewContext)
newPerson.name = "Xi"
newPerson.age = 10
newPerson.sex = "male"
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
Button("Change Age & Sex in Background Context") {
guard let objectId = people.first?.objectID else { return }
let container = persistenceController.container
container.performBackgroundTask { bgContext in
Task {
await bgContext.perform {
if let person = bgContext.object(with: objectId) as? Person {
person.age = 111
person.sex = "female"
try? bgContext.save()
}
}
}
}
}
此时:
Person(name="Xi", age=111, sex="female")
Person(name="Xi", age=10, sex="male")
Button("Change Age & Sex in ViewContext") {
guard let person = people.first else { return }
person.name = "XiXi"
person.age = 222
try? viewContext.save()
}
save 之后,viewContext 中的对象为 Person(name="XiXi", age=111, sex="female")
name 采用了上下文中的值,age 和 sex 采用了store 中的值。
之后重新阅读了文章最后一段,发现肘子的操作步骤为:
其实在执行第一步的时候,name 和 age 已经持久化了,变成 store 里面的值了。 NSBatchUpdaterequest 因为不涉及到 context 而直接修改 store,应该不会才生冲突才对。
因此我感觉最后一句话的表述可能不太准确。
对不起,我才发现,文章中例子的合并规则标注错误。
我一直以为写的是 NSMergeByPropertyObjectTrumpMergePolicy ,结果文章中的代码竟然写成了 NSMergeByPropertyStoreTrumpMergePolicy 。
是的,你现在的测试结果对于 NSMergeByPropertyStoreTrumpMergePolicy 是完全正确的。
感谢指出错误,避免给大家造成更大的困扰。
文章中已经改回 NSMergeByPropertyObjectTrumpMergePolicy
2022年5月25日 11:48,beader @.***> 写道:
@fatbobman https://github.com/fatbobman 上下文数据在保存时,会发现它的快照版本已经与持久化中的数据版本不一样。按照NSMergeByPropertyStoreTrumpMergePolicy 规则进行比对。因为 上下文仅改动了两个属性 name 和 sex ,因此对于上下文来说,仅有这两个属性会出现冲突的情况,且一旦出现冲突将采用上下文版本的属性内容。 而,如果设置成 NSOverwriteMergePolicy , 则 三个属性都将使用上下文版本的属性内容。
逐个比对的意思是并非哪个数据版本新就使用那个,而是说是否保留没有发生冲突的属性
我做了如下实验。在 CoreData Stack 的设置中:
container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy container.viewContext.automaticallyMergesChangesFromParent = false
这么设置后,background context 的数据持久化之后,viewContext 不会自动 merge,此时 viewContext 再次 save 时,则会产生冲突
Step1: 在 viewContext 下生成一个 Person(name="Xi", age=10, sex="male"),保存,并存放在 people 中
@FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Person.name, ascending: true)], animation: .default) private var people: FetchedResults
...
private func addPerson() { withAnimation { let newPerson = Person(context: viewContext) newPerson.name = "Xi" newPerson.age = 10 newPerson.sex = "male" do { try viewContext.save() } catch { let nsError = error as NSError fatalError("Unresolved error (nsError), (nsError.userInfo)") } } } Step 2: 在 background context 中修改该 Person
Button("Change Age & Sex in Background Context") { guard let objectId = people.first?.objectID else { return } let container = persistenceController.container container.performBackgroundTask { bgContext in Task { await bgContext.perform {
if let person = bgContext.object(with: objectId) as? Person { person.age = 111 person.sex = "female" try? bgContext.save() } } } } } 此时:数据库中为 Person(name="Xi", age=111, sex="female") viewContext 中依然是 Person(name="Xi", age=10, sex="male") Step 3: 在 ViewContext 中修改 name 和 age
Button("Change Age & Sex in ViewContext") { guard let person = people.first else { return } person.name = "XiXi" person.age = 222 try? viewContext.save() } save 之后,viewContext 中的对象为 Person(name="XiXi", age=111, sex="female")
name 采用了上下文中的值,age 和 sex 采用了store 中的值。
之后重新阅读了文章最后一段,发现肘子的操作步骤为:
上下文中修改了 name 和 age NSBatchUpdaterequest 中修改了 age 和 sex 其实在执行第一步的时候,name 和 age 已经持久化了,变成 store 里面的值了。 NSBatchUpdaterequest 因为不涉及到 context 而直接修改 store,应该不会才生冲突才对。
因此我感觉最后一句话的表述可能不太准确。
— Reply to this email directly, view it on GitHub https://github.com/fatbobman/blogComments/issues/125#issuecomment-1136694130, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANIYIGNV3JXSPFSERKT645TVLWPJXANCNFSM5HMU7A5Q. You are receiving this because you were mentioned.
你好 谢谢你的文章给我帮助很大 问一个小白问题 是否所有Core Data操作都需要在子线程进行 一些很简单的保存更新是否可以在主线程进行
你好 谢谢你的文章给我帮助很大 问一个小白问题 是否所有Core Data操作都需要在子线程进行 一些很简单的保存更新是否可以在主线程进行
当前的 Core Data 已经支持了上下文数据的自动合并,因此使用私有上下文比以前的工作量小些。 事实上,几乎所有的操作都可以在视图上下文( 主线程 )中进行。使用私有上下文最大的作用是避免对主线程造成更大的负担。
你好 谢谢你的文章给我帮助很大 问一个小白问题 是否所有Core Data操作都需要在子线程进行 一些很简单的保存更新是否可以在主线程进行
当前的 Core Data 已经支持了上下文数据的自动合并,因此使用私有上下文比以前的工作量小些。 事实上,几乎所有的操作都可以在视图上下文( 主线程 )中进行。使用私有上下文最大的作用是避免对主线程造成更大的负担。
background context
还有个应用场景是在编辑页面中,对NSManagedObject进行比较大的改动,譬如增加 relationship 之类的,且希望当用户点击保存时,才真的进行持久化操作。如果用户点了取消,则直接把临时创建的context丢弃即可。直接在 viewContext 操作的话,则需要再进行一些清理动作。
@fatbobman
你好 谢谢你的文章给我帮助很大 问一个小白问题 是否所有Core Data操作都需要在子线程进行 一些很简单的保存更新是否可以在主线程进行
当前的 Core Data 已经支持了上下文数据的自动合并,因此使用私有上下文比以前的工作量小些。 事实上,几乎所有的操作都可以在视图上下文( 主线程 )中进行。使用私有上下文最大的作用是避免对主线程造成更大的负担。
感谢指点,我现在是把一些简单的不耗时的Core Data操作在主线程,耗时的丢子线程
感谢博主分享,学到很多。🙏
有一个问题想请教下,通过 NSFetchRequest
查询出来的 NSManagedObject
对象拿到其他队列去使用的时候,比如只用来 get 某个属性(例如有个属性叫 name
),但是不对这个对象进行任何 set 操作,这样也会有线程问题吗?
感谢博主分享,学到很多。🙏
有一个问题想请教下,通过
NSFetchRequest
查询出来的NSManagedObject
对象拿到其他队列去使用的时候,比如只用来 get 某个属性(例如有个属性叫name
),但是不对这个对象进行任何 set 操作,这样也会有线程问题吗?
有。只能在该托管对象绑定的上下文所在的线程中进行。 也就是说,在私有上下文中获取的数据,不能在视图中读取它的属性。
@fatbobman
感谢博主分享,学到很多。🙏
有一个问题想请教下,通过
NSFetchRequest
查询出来的NSManagedObject
对象拿到其他队列去使用的时候,比如只用来 get 某个属性(例如有个属性叫name
),但是不对这个对象进行任何 set 操作,这样也会有线程问题吗?有。只能在该托管对象绑定的上下文所在的线程中进行。 也就是说,在私有上下文中获取的数据,不能在视图中读取它的属性。
好的,感谢博主解答 🫡
https://www.fatbobman.com/posts/concurrencyOfCoreData/
Swift 5.5 提供了盼望已久的 async/await 的功能,为多线程开发带来了前所未有的便利。但 Core Data 由于其特有的并发规则,使用不慎容易导致代码陷入不可控状态,因此让不少开发者对在 Core Data 中进行多线程开发产生了望而却步的情绪。本文将对 Core Data 并发编程中几个常见的问题予以提示,以便开发者更好地了解 Core Data 的并发规则,充分享受 Core Data 提供的强大功能。