我为一个表的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,只是为了避免这种情况。我不确定它是否会降低性能。将在明天更新进展。
答案 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)
我会在不同的队列中执行服务器同步,因此它不会在本地阻碍用户体验(您也可以在主线程上进行本地更改,并且只在后台同步)
您与服务器的同步可能会出现问题,因为未跟踪本地更改,并且在应用程序启动之间不会保持将对象同步到服务器的需要(保存到存储成功但同步到服务器中断/失败等等)。 )。