1

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

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

首先,我尝试按如下方式创建我的临时对象;

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

在决定是否保留该对象后,我就简单了;

[privateContext insertObject:user];

在我使应用程序多线程之前,这很好用,但是现在在稍微拆开东西并通过子/父上下文添加多线程并发之后,结果并不像预期的那样。

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

但仔细观察 mainContext 中的 THAT registeredObject,发现它已被清空。没有内容。根据类型,所有属性都是 nil 或 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];

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

4

1 回答 1

2

据我所知,这是另一个 CoreData 错误。
我可以理解它的“如何”,但不能理解它的“为什么”。

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

所以我制定了一个狡猾的计划: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];
    }
}

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

但是,我可能会尝试完全避免这种类型的插入。
我真的不明白您需要将对象插入特定上下文并让它挂起,直到您决定是否需要它。

总是将对象插入上下文不是更容易吗(如果需要长时间将值保存在字典中)。但是当您决定该对象不应该“进入商店”时,只需将其删除?

(这被称为除草 BTW)

于 2014-02-23T19:22:24.647 回答