NSOperationQueue,CoreData和MagicalRecord同步问题

时间:2014-02-11 01:14:09

标签: ios multithreading core-data nsoperationqueue magicalrecord

我为一个表的NSOperationQueue的所有操作创建inserting/updating/deleting/searching。我使用MagicalRecord进行核心数据操作。但我对同步有一些问题。一个简化的例子如下。

EG。一个名为person的表和一个名为like的人员列。当用户单击按钮时,like将增加1。我喜欢

[SameBackgroundQueue addOperationWithBlock:^{
    User *user = [User MR_findFirstWithPredicate:some_predicate];
    user.like += 1;
    NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];
    [localContext MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
        [SameBackgroundQueue addOperationWithBlock:^{
            [self syncWithServer];//This can take time
    }];
}];

但是,如果用户快速点击,那么类似的将不正确,因为MR_findFirstWithPredicate可以获得脏记录。问题源于NSOperationQueue可以雇用不同的线程,MR_findFirstWithPredicate使用当前线程的上下文。因此,它可能会尝试从不同的NSManagedObjectContext获取用户,因此会获得脏数据。

当然,如果我们使用mainQueue,我们就不会有任何问题。但是,我如何使用后台线程并确保我没有脏记录问题。如果我在一个特定的线程而不是用户NSOperationQueue上运行,似乎可以解决整个问题。我应该改用GCD吗?


在我以前的项目中,我正在使用

[self createManagedObjectContextWithParent:self.mainQueueManagedObjectContext andConcurrencyType:NSPrivateQueueConcurrencyType];

[managedObjectContext save:&error];
if (managedObjectContext.parentContext) {
    [managedObjectContext.parentContext performBlock:^{
        NSError *parentError = nil;
        [managedObjectContext.parentContext save:&parentError];
    }];
}

我知道有WWDC视频谈论NSManagedObjectContext和并发上下文等。但是如果我使用它,我就不能使用MagicRecord。

任何建议都将受到高度赞赏。


实际上我找到了一种更有效的方法。如果我做错了,请纠正我。

我实际上为User OperationQueue创建了一个共享的单例上下文[NSManagedObjectContext MR_context]。因此,即使所有线程都访问相同的上下文,它们也不会获得脏数据。

仍然会出现故障,例如,如果两个线程正在更改同一上下文的同一对象,那该怎么办?这通常是一种非常罕见的情况,但我会看到它是怎么回事。我可以将最大并发线程设置为1,只是为了避免这种情况。我不确定它是否会降低性能。将在明天更新进展。

2 个答案:

答案 0 :(得分:3)

我建议将操作块更改为更像这样的内容:

[TheSameQueue addOperationWithBlock:^{
    NSManagedObjectContext *localContext = [NSManagedObjectContext MR_context];

    User *user = [User MR_findFirstWithPredicate:some_predicate inContext:localContext];
    user.like += 1;

    [localContext MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
        [self syncWithServer];//This can take time
    }];
}];

您正在对默认上下文执行所有后台操作,您可以将其视为主线程上下文。一般来说,这将在99%的时间内起作用,但会导致随机死锁和崩溃。此外,不推荐使用MR_contextForCurrentThread,因为它还会导致队列出现问题,即为线程使用错误的上下文。

答案 1 :(得分:2)

为了避免这种竞争条件,您可以通过设置一次将NSOperationQueue限制为一次操作:

[TheSameQueue setMaxConcurrentOperationCount:1];

将其渲染为串行执行队列。

您还可以使用GCD创建一个串行队列(但是您将更难以取消并设置操作的依赖关系。)

(两者都在后台使用GCD)

我会在不同的队列中执行服务器同步,因此它不会在本地阻碍用户体验(您也可以在主线程上进行本地更改,并且只在后台同步)

您与服务器的同步可能会出现问题,因为未跟踪本地更改,并且在应用程序启动之间不会保持将对象同步到服务器的需要(保存到存储成功但同步到服务器中断/失败等等)。 )。