在后台线程上保存后,多线程核心数据 object 在主线程上具有空属性
Multithreaded Core Data object has empty properties on main thread after saving on background thread
正如标题中所描述的,我在后台线程上保存了一个object后,它的属性在主线程上是空的,比如字符串是""
,数字是0
等等
这是一些代码!
用户class:
@objc(User)
class User: NSManagedObject {
@NSManaged var id: Int32
@NSManaged var email: String
}
发生实际保存的 UserRepository:
func saveUser(fromJSON json: Any, onSuccess: ((User) -> Void)?, onFailure: ((Error) -> Void)?) {
dataManager.persistentContainer.performBackgroundTask { context in
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
let user = self.userFactory.user(fromJSON: json, inContext: context)
// print(user.id) and print(user.email) output correct data
do {
try context.save()
DispatchQueue.main.async {
// print(user.id) and print(user.email) show 0 and ""
onSuccess?(user)
}
} catch let error {
DispatchQueue.main.async {
onFailure?(error)
}
}
}
}
其中dataManager
是这样配置的:
class CoreDataManager {
private let modelName: String
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: modelName)
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
})
return container
}()
init(modelName: String) {
self.modelName = modelName
}
}
我猜 userFactory
没那么重要,但为了完整起见:
class UserFactory {
func user(fromJSON json: Any, inContext context: NSManagedObjectContext) -> User {
guard
let jsonDictionary = json as? [String: Any?],
let userDictionary = jsonDictionary["user"] as? [String: Any?],
let id = userDictionary["id"] as? Int32,
let email = userDictionary["email"] as? String
else {
fatalError("Could not parse User object.")
}
let user = User(context: context)
user.id = id
user.email = email
return user
}
}
请参阅上面片段中关于打印语句的注释,这些语句指出了用户属性良好的地方以及它们显示 0
和 ""
.
的地方
我知道我不能在线程之间传递 NSManagedObjects,但即使我尝试使用 objectID
在 DispatchQueue.main.async
块中获取用户 object (NSManagedObjectID
), 用户属性仍然是空的。
我想我做错了什么,但不能指手画脚。任何建议将不胜感激。
非常感谢!
我认为问题在于 Core Data 不是线程安全的。我知道你知道这一点 - 毕竟,这就是你创建特殊背景上下文的原因 - 但它具有非常严格的含义。你是说:
dataManager.persistentContainer.performBackgroundTask { context in
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
let user = self.userFactory.user(fromJSON: json, inContext: context)
do {
try context.save()
DispatchQueue.main.async {
print(user) // <--
你不能那样做。您仍在谈论保存到后台上下文中的 user
,但您已经切换了线程。不可以。你不能从两个不同的线程谈论这个 user
。
一旦进入主线程,与 Core Data 对话的唯一方式就是通过 main 上下文。您需要通过用户 ID 从 main 上下文中 fetch 用户。现在您可以看看它是如何填充的。
正如标题中所描述的,我在后台线程上保存了一个object后,它的属性在主线程上是空的,比如字符串是""
,数字是0
等等
这是一些代码!
用户class:
@objc(User)
class User: NSManagedObject {
@NSManaged var id: Int32
@NSManaged var email: String
}
发生实际保存的 UserRepository:
func saveUser(fromJSON json: Any, onSuccess: ((User) -> Void)?, onFailure: ((Error) -> Void)?) {
dataManager.persistentContainer.performBackgroundTask { context in
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
let user = self.userFactory.user(fromJSON: json, inContext: context)
// print(user.id) and print(user.email) output correct data
do {
try context.save()
DispatchQueue.main.async {
// print(user.id) and print(user.email) show 0 and ""
onSuccess?(user)
}
} catch let error {
DispatchQueue.main.async {
onFailure?(error)
}
}
}
}
其中dataManager
是这样配置的:
class CoreDataManager {
private let modelName: String
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: modelName)
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
})
return container
}()
init(modelName: String) {
self.modelName = modelName
}
}
我猜 userFactory
没那么重要,但为了完整起见:
class UserFactory {
func user(fromJSON json: Any, inContext context: NSManagedObjectContext) -> User {
guard
let jsonDictionary = json as? [String: Any?],
let userDictionary = jsonDictionary["user"] as? [String: Any?],
let id = userDictionary["id"] as? Int32,
let email = userDictionary["email"] as? String
else {
fatalError("Could not parse User object.")
}
let user = User(context: context)
user.id = id
user.email = email
return user
}
}
请参阅上面片段中关于打印语句的注释,这些语句指出了用户属性良好的地方以及它们显示 0
和 ""
.
我知道我不能在线程之间传递 NSManagedObjects,但即使我尝试使用 objectID
在 DispatchQueue.main.async
块中获取用户 object (NSManagedObjectID
), 用户属性仍然是空的。
我想我做错了什么,但不能指手画脚。任何建议将不胜感激。
非常感谢!
我认为问题在于 Core Data 不是线程安全的。我知道你知道这一点 - 毕竟,这就是你创建特殊背景上下文的原因 - 但它具有非常严格的含义。你是说:
dataManager.persistentContainer.performBackgroundTask { context in
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
let user = self.userFactory.user(fromJSON: json, inContext: context)
do {
try context.save()
DispatchQueue.main.async {
print(user) // <--
你不能那样做。您仍在谈论保存到后台上下文中的 user
,但您已经切换了线程。不可以。你不能从两个不同的线程谈论这个 user
。
一旦进入主线程,与 Core Data 对话的唯一方式就是通过 main 上下文。您需要通过用户 ID 从 main 上下文中 fetch 用户。现在您可以看看它是如何填充的。