核心数据不更新相关对象



我有一个类a,它有一个";对许多";与B.的关系。A及其Bs是从REST端点获取的,我们使用这种方法来创建对象。

A具有唯一的ID和一些其他属性(示例中未显示(。B有一个ID(bID(,当与A组合时是唯一的,并且与A的关系称为A;uniqueID";。对于B,约束是"0";a、 bID"-我们希望每个B只存在于A和bID的每个组合中。A对其Bs的删除规则是";级联";。上下文和所有子上下文的合并策略为NSOverwriteMergePolicy

代码看起来像这样:

public class A: NSManagedObject, Decodable {
enum CodingKeys: String, CodingKey {
case uniqueID, bData
}
public required convenience init(from decoder: Decoder) throws {
guard let context = decoder.userInfo[CodingUserInfoKey.managedObjectContext!] as? NSManagedObjectContext else {
throw MyCoreDataError.missingManagedObjectContext
}
self.init(context: context)
let values = try decoder.container(keyedBy: CodingKeys.self)
uniqueID = try values.decode(String.self, forKey: .uniqueID)
bData = try NSSet(set: values.decode(Set<B>.self, forKey: .bData))
}
}
public class B: NSManagedObject, Decodable {
enum CodingKeys: CodingKey {
case bID, someData
}
public required convenience init(from decoder: Decoder) throws {
guard let context = decoder.userInfo[CodingUserInfoKey.managedObjectContext            
throw MyCoreDataError.missingManagedObjectContext
}
self.init(context: context)
let values = try decoder.container(keyedBy: CodingKeys.self)
bID = try values.decode(String.self, forKey: .bID)
someData = try values.decode(String.self, forKey: .someData)
}
}

这对于最初的获取来说很好,但是过了一段时间,我们会在更新后再次获取A和它的B。A已成功更新,但B未更新。查看SQLite数据,B只是被忽略了。对象肯定会在内存中更新(我可以看到新的B具有正确的属性值(,但当保存上下文时,它们不会持久化。

如果我删除了B约束,那么新的B将被持久化,但它们不会替换旧的B。

如果我在设置bData之前保存上下文,那么新的B将被持久化,但旧的B不会被删除(尽管它们与A的关系被设置为NULL(。

核心数据在合并更改的A和更改的B的同时似乎存在某种问题。有人知道是否有一个干净的解决方案吗?

我使用以下扩展来解决这个问题:

extension Identifiable where Self: NSManagedObject {
init(fetchOrCreate id: ID, context: NSManagedObjectContext) {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: String(describing: Self.classForCoder()))
request.predicate = NSPredicate(format: "id == (id)")
request.fetchLimit = 1
if let result = (try? context.execute(request) as? NSAsynchronousFetchResult<Self>)?.finalResult?.first
{
self = result
return
}
self.init(context: context)
setValue(id, forKeyPath: "id")
}
}

因此,如果数据库中已经存在现有对象,我的初始值设定项将返回该对象,或者创建一个新对象。

并像这样使用:

public class B: NSManagedObject, Decodable {
enum CodingKeys: CodingKey {
case bID, someData
}
public required convenience init(from decoder: Decoder) throws {
guard let context = decoder.userInfo[CodingUserInfoKey.managedObjectContext            
throw MyCoreDataError.missingManagedObjectContext
}
self.init(
fetchOrCreate: try values.decode(String.self, forKey: .bID),
context: context
)
let values = try decoder.container(keyedBy: CodingKeys.self)
someData = try values.decode(String.self, forKey: .someData)
}
}

我不知道你为什么要为你的id使用不同的名称(uniqueIDbID(——如果你的生产代码中真的有这个名称,那么NSManagedObject的快速扩展不会让你那么容易地返回提取的对象:self = result会产生一个错误:Cannot assign to value: 'self' is immutable

因此,要使用自定义id密钥,您可以创建一些可以扩展的自定义协议,而不是Identifiable,或者使用ObjC扩展,它不会有这样的限制:

@implementation NSManagedObject (initOrGet)
- (instancetype)initOrGet:(NSObject *)id
key:(NSString *)key
context:(NSManagedObjectContext*)moc
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass(self.class).pathExtension];
request.predicate = [NSPredicate predicateWithFormat:@"%@ == %@", key, id];
request.fetchLimit = 1;
NSError *error;
NSManagedObject *result = [moc executeFetchRequest:request error:&error].firstObject;
if (result != nil) {
return result;
}
self = [self initWithContext:moc];
[self setValue:id forKey:key];
return self;
}
@end

最新更新