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

时间:2014-02-11 22:45:58

标签: ios objective-c multithreading nsmanagedobject nsmanagedobjectcontext

我正在使用Core Data处理多线程应用。具有讽刺意味的是,当我得知Core Data不是线程安全的时候,我认为该应用程序即将完成...因此,我现在正在添加多上下文而不是我从Xcode模板中获得的单上下文(并且到目前为止一直工作,真的,但这比运气更有运气我想)

我正在尝试使用>具有父/子上下文的iOS 5.0方法很适合我正在尝试的内容,但是当我在子上下文中插入具有有效数据/属性的有效对象时,它们都变为nil或0(取决于属性)在父上下文中输入。

我注意到有类似的帖子,但没有任何答案;

Parent MOC get changes with empty data from child MOC

NSManagedObject values are correct, then incorrect when merging changes from parent to child NSManagedObjectContext

这是一些获得想法的代码;

我有一个单独的管理器,UI用来“做东西”,然后在任务完成时使用委托或回调。该经理反过来有一个模型经理来处理持久数据管理,还有一些其他的通信经理与Web / REST-API等交谈。

- (void) doSomeStuff:(NSString*)someParam
      callbackObject:(NSObject*)object
           onSuccess:(SEL)successSelector
           onFailure:(SEL)failureSelector
{
    //Kick as an async thread since we don't want to disturb the UI
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), ^(void)
    {
        //Ask model manager for a nice context to work with... 
        //NOTE; All contexts (private ones) are stored in a NSDictionary using currentThread as key, so that we always can access the "correct" context at anytime if we need to insert/delete/change anything in the context
        NSManagedObjectContext* privateContext = [self.modelManager getManagedObjectContext];

        //Perform stuff in block since our context is of NSPrivateQueueConcurrencyType
        [privateContext performBlockAndWait:^{

            //... do the actual stuff, go online, talk to a REST-API, wait for things, which will eventually result in a new object being created
            User* user = ...;

            //Store object in model manger which is my abstraction of CoreData
            //NOTE: the modelManager will get a reference to the currently used privateContext and use it to insert the object
            [self.modelManager addUser:user];

            //Save model!
            [self.modelManager save];
        }];

        //Trigger callback, if applicable 
        if (loggedInUserGuid && successSelector)
        {
            [object performSelectorOnMainThread:successSelector withObject:loggedInUserGuid waitUntilDone:NO];
        }
    });
}

modelManager中的save函数考虑了context concurrencyType,并且在使用子/父上下文时将根据规范行事;

- (void) save
{
    //Get current context...
    NSManagedObjectContext* currentContext = [self getManagedObjectContext];

    //If the current context has any changes...
    if ([currentContext hasChanges])
    {
        //Changes detected! What kind of context is this?
        switch (currentContext.concurrencyType)
        {
            case NSPrivateQueueConcurrencyType:
            {
                NSError* error = nil;
                if (![currentContext save:&error])
                    abort();

                if (self.mainManagedObjectContext hasChanges])
                {
                    [self.mainManagedObjectContext performBlockAndWait:^{

                        NSError *mainError;
                        if (![self.mainManagedObjectContext save:&mainError])
                            abort();

                    }];
                }

                break;
            }

            ....

        }
    }
}

通过在保存子上下文和父/主上下文之前和之后添加调试打印,我注意到插入的对象很好地存在于子上下文中,并且子上下文说“hasChanges == YES”,而正如预期的那样,main-context说“hasChanges == NO”。

(entity: User; id: 0x10c06c8c0 <x-coredata:///User/tDFBBE194-44F9-44EC-B960-3E8E5374463318> ; data: {
    created = nil;
    emailAddress = "wilton@millfjord.se";
    firstName = Wilton;
    guid = "2eaa77fa-0d2c-41b8-b965-c4dced6eb54a";
    lastName = Millfjord;
    nbrOfOfflineKeys = 5;
    password = 123456;
})

此后保存主要上下文,并在保存之前查看它的registeredObjects,我们可以看到“hasChanges == YES”(在孩子将插入的对象保存/合并到其父级之后预期。但是,此外 - 根据属性类型,所有参数和属性现在都为零或0;

(entity: User; id: 0x10c06c8c0 <x-coredata:///User/tDFBBE194-44F9-44EC-B960-3E8E5374463318> ; data: {
    created = nil;
    emailAddress = nil;
    firstName = nil;
    guid = nil;
    lastName = nil;
    nbrOfOfflineKeys = 0;
    password = nil;
})

如您所见,ID是相同的,因此它是“相同”对象,但没有/重置内容。我已经尝试了“setMergePolicy”的所有各种组合但没有效果。

我甚至尝试过&lt;我在“mergeChangesFromContextDidSaveNotification”中添加NSNotificationCentre方法的iOS 5.0方法,我唯一可以验证的是,NSNotification参数中的数据是有效的和“好数据”,但主要上下文仍未正确更新。结果仍然是一个空对象。

期待您的想法和想法。

/马库斯

更新

使用主上下文创建新托管对象时使用的代码,无论当时正在运行哪个线程/上下文执行块...

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

此后,对象用户挂起时没有上下文,直到稍后我决定将其添加到托管上下文(使用我的 getManagedObjectContext 来指定正确/当前上下文)。

[[self getManagedObjectContext] insertObject:user];

1 个答案:

答案 0 :(得分:0)

在尝试了所有内容之后,慢慢地将现有代码减少为空,只是一个没有函数或管理器的直接内联版本 - 只是为了确定在保存子上下文后我可以看到mainContext中的registeredObjects ,实际上不是一个空元素......我终于找到了答案。

事实证明,我正在创建(故意)在创建时未被管理(放入上下文)的对象。我的想法是,当我使用REST-API /后端时,我可以将JSON响应转换为对象(悬空,没有上下文),然后与我已经存储在模型管理器中的内容进行比较,以便我可以检测更改并通知用户这些更改......

因此,我做了;

//Create object "hanging" so that we can add it to context alter on...
NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:mainContext];
User* user = (User *)[[User alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

......然后,当我认为这是我想保留的新东西和事物时;

//Insert it
[privateContext insertObject:user];

但是,插入的对象在保存在子上下文中时会自动合并到父的主上下文中 - 清空!但是当我尝试将对象直接添加到私有上下文时,该对象在合并期间突然没有被清空;

//Create object AND insert into context now...
NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:mainContext];
User* user = (User *)[[User alloc] initWithEntity:entity insertIntoManagedObjectContext:privateContext];

我不明白为什么,真的,但确实如此。合并对象在从子级私有上下文合并到父级主上下文时被清空的原因。我宁愿这不是这种情况,因为这意味着我正在运行的“更改检测/通知”代码的大量重组,但至少我对CoreData来说是线程安全的;)