临时托管对象没有从子上下文正确地合并到主上下文



我有一个多线程应用程序,需要将专用上下文合并到主上下文,主上下文又连接到持久存储控制器。

我还需要创建不受管理的临时对象(直到我后来决定管理它们)。

首先,我尝试创建我的临时对象,如下所示;

NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:myMainQueueContext];
User* user = (User *)[[User alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

在决定是否保留这个物体之后,我简单地;

[privateContext insertObject:user];

在我使应用程序成为多线程应用程序之前,这非常有效,但现在,在稍微拆开一些东西并添加了子/父上下文的多线程并发之后,结果并不像预期的那样。

通过查看上下文的"registeredObjects",我可以看到我创建的、现在插入的用户是在privateContext中管理的。保存后,mainContext相应地发生了变化,我可以看到它有changes,并且registeredObjects中现在有一个对象。

但是仔细查看mainContext中的那个registeredObject,就会发现它已经清空了。没有内容。根据类型的不同,所有属性都为零或0。因此,人们会认为这可能是因为objectId不相同。。。但事实确实如此;(这是同一个物体。但没有内容。

我试图在这里的另一篇帖子中获得一些关于这个问题的意见,但没有成功。

子上下文对象在合并到父/主上下文后变为空

无论如何,通过改变我创建对象的方式,我终于让事情运转起来了;

User* user = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:privateContext];

突然间,我的子对象被合并到mainContext中,而没有丢失它们的内容,原因我不得而知,但不幸的是,这也导致了我无法再创建临时非托管对象;(我读到Marcus Zarra在创建非托管对象时支持我的第一种方法,但这不适用于在我的多线程应用程序中合并上下文…

期待任何想法和想法——我是不是唯一一个试图在异步工作线程中创建临时对象的人,我只想管理/合并其中的一个子集到mainContext?

编辑

具体的代码显示什么是有效的,更重要的是什么是无效的;

//Creatre private context and lnk to main context..
NSManagedObjectContext* privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
//Link private context to main context...
privateManagedObjectContext.parentContext = self.modelManager.mainManagedObjectContext;
[privateManagedObjectContext performBlock:^()
{
//Create user
NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:self.modelManager.mainManagedObjectContext];
User* user = (User *)[[User alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
[user setGuid:@"123123"];
[user setFirstName:@"Markus"];
[user setLastName:@"Millfjord"];
[privateManagedObjectContext insertObject:user];
//Debug before we start to merge...
NSLog(@"Before private save; private context has changes: %d", [privateManagedObjectContext hasChanges]);
NSLog(@"Before private save; main context has changes: %d", [self.modelManager.mainManagedObjectContext hasChanges]);
for (NSManagedObject* object in [privateManagedObjectContext registeredObjects])
NSLog(@"Registered private context object; %@", object);
//Save private context!
NSError* error = nil;
if (![privateManagedObjectContext save:&error])
{
//Oppps
abort();
}
NSLog(@"After private save; private context has changes: %d", [privateManagedObjectContext hasChanges]);
NSLog(@"After private save; main context has changes: %d", [self.modelManager.mainManagedObjectContext hasChanges]);
for (NSManagedObject* object in [privateManagedObjectContext registeredObjects])
NSLog(@"Registered private context object; %@", object);
for (NSManagedObject* object in [self.modelManager.mainManagedObjectContext registeredObjects])
NSLog(@"Registered main context object; %@", object);
//Save main context!
[self.modelManager.mainManagedObjectContext performBlock:^()
{
//Save main context!
NSError* mainError = nil;
if (![self.modelManager.mainManagedObjectContext save:&mainError])
{
//Opps again
NSLog(@"WARN; Failed saving main context changes: %@", mainError.description);
abort();
}
}];
}];

上述方法不起作用,因为它创建了一个临时对象,然后将其插入上下文中。然而,这个轻微的mod使事情正常工作,但阻止我拥有临时对象。。。;

NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:self.modelManager.mainManagedObjectContext];
User* user = (User *)[[User alloc] initWithEntity:entity insertIntoManagedObjectContext:privateManagedObjectContext];

因此,我想知道;有什么区别?很明显,肯定有一些不同,但我不明白

据我所知,这是另一个CoreData错误
我能稍微理解它的"如何",但不能理解其"为什么"。

正如您所知,CoreData严重依赖KVO。托管上下文像鹰一样观察对象的变化
由于"临时"对象没有上下文,上下文在附加到它之前无法跟踪它们的更改,因此它不会正确(或根本不会)向父上下文报告更改。因此,一旦您使用insertObject:将对象插入到上下文中,父上下文将获得插入对象的"提交值",该值将变为nil(我想这就是错误)

因此,我制定了一个狡猾的计划:D
我们将迅速摆脱困境

介绍NSManagedObjectContext+fix.m:

//Tested only for simple use-cases (no relationship tested)
+ (void) load
{
Method original = class_getInstanceMethod(self, @selector(insertObject:));
Method swizzled = class_getInstanceMethod(self, @selector(__insertObject__fix:));
method_exchangeImplementations(original, swizzled);
}
- (void) __insertObject__fix:(NSManagedObject*)object
{
if (self.parentContext && object.managedObjectContext == nil) {
NSDictionary* propsByName = [object.entity propertiesByName];
NSArray* properties = [propsByName allKeys];
NSDictionary* d = [object committedValuesForKeys:properties];
[propsByName enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSPropertyDescription* prop, BOOL *stop) {
if ([prop isKindOfClass:[NSAttributeDescription class]]) {
[object setValue:[(NSAttributeDescription*)prop defaultValue] forKey:key];
} else if ([prop isKindOfClass:[NSRelationshipDescription class]]) {
[object setValue:nil forKey:key];
}
}];
[self __insertObject__fix:object];
[object setValuesForKeysWithDictionary:d];
} else {
[self __insertObject__fix:object];
}
}

这可能会帮助您使代码更加简洁

然而,我可能会尽量避免这种类型的插入
我真的不理解您是否需要将对象插入到特定上下文中,并将其挂起,直到您决定是否需要它

ALWAYS将对象插入上下文(如果需要长时间将值保存在字典中)不是更容易吗。但当你决定对象不应该"击中商店"时,简单地删除它

(这被称为除草BTW)

最新更新