问题:如何使“核心日期”合并在两个不同线程中对相同NSManagedObject所做的更改?线程更改不同的属性,并且这些属性的任何组合都是有效的。

有一个具有(至少)两个线程的应用程序,UI线程和后台线程。
所谓的DocumentNSManagedObject的子类。Document具有三个属性,attrAattrBattrC

后台线程读取attrA并写入attrB,例如:

doc.attbB = md5(doc.attrA);

(在实践中,它更复杂,更耗时,但是您可以理解)。

UI线程向用户显示所有attrAattrBattrC,并允许用户更改attrAattrC。 (并且在某些时候attrB的值无效。)

我强调指出,只有UI线程才写入attrA,只有后台线程才写入attrB

现在,用户在完成attrC的计算之前更改attrB
后台线程尝试保存attrB并得到一个错误。
我现在所做的是:
if(!saved) {
    // I did try to check that it's *that* kind of error,
    // but that's iOS5-specific, while I need 4.3
    // (comments on error type checking in 4.3 are welcome).
    // Anyway, finally it was this:
    id tmp = doc.attrB;
    [[doc managedObjectContext] refreshObject:doc mergeChanges:NO];
    doc.attrB = tmp;
    BOOL saved = [context save:&error];
    // NSLog if it still failed
}

在一般情况下,这是行不通的。

首先,如果在这些行之间进行更多更改会发生什么:
    [[doc managedObjectContext] refreshObject:doc mergeChanges:NO];
    doc.attrB = tmp;
    // more changes happen here!!!
    BOOL saved = [context save:&error];

是的,我可以暂时替换一下,但这不是一般情况下的解决方案。如果更改重复发生,则可能是一个无休止的循环。

第二,UI线程也可能正在尝试保存某些内容。在其中一个类中,我看到了旨在合并更改的代码,
#pragma mark Changes Propagation

- (void)__contextDidSave:(NSNotification*)notification
{
    [parent performSelectorOnMainThread:@selector(__mergeChanges:) withObject:notification waitUntilDone:NO];
}

- (void)__mergeChanges:(NSNotification*)notification
{
    [objectContext mergeChangesFromContextDidSaveNotification:notification];
    if (parent) {
        [parent __mergeChanges:notification];
    }
}

据我了解,__mergeChanges将在正确的线程上运行,但不会立即运行;在后台线程更改attrA之后但在运行attrC之前,UI线程可能会尝试保存attrB__mergeChanges中的更改。

这是经典的比赛条件。

问题:如何正确 使Core Date合并在两个不同线程中对相同NSManagedObject所做的更改? (线程更改了不同的属性,并且这些属性的任何组合都是有效的,但是Core Data不知道。)

最佳答案

我不是核心数据专家,但是我猜您可以使用synced指令:

// UI Thread
@synchronized(AN_INSTANCE_OF_ANY_OBJECT) {
    // your first thread's code goes here
}

// Background Thread
@synchronized(THE_SAME_INSTANCE_OF_THE_OBJECT) {
    // your second thread's code goes here
}

如果任何其他线程正在执行其作用域,则这应该停止任何线程的执行,请注意该实例应该相同,因此,如果无法访问该实例,则可以使用公共(public)静态实例,这通常可以帮助我完成此操作。

09-11 19:28