2

我的应用程序有一个竞争条件,多个 API 请求可能会返回完全相同的数据并尝试保存它们。我想通过在我的模型上添加 validateForInsert 来防止这种情况发生。验证的前提只是检查并查看标识符键是否已经像这样存在

- (BOOL)validateForInsert:(NSError *__autoreleasing *)error
{
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([CWDeal class])];
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"identifier == %@", self.identifier];
    NSError *validateError;
    int count = [[(CWAppDelegate *)[[UIApplication sharedApplication] delegate] privatewriterManagedObjectContext] countForFetchRequest:fetchRequest error:&validateError];
    if (count > 0) {
        return FALSE;
    }
    return [super validateForInsert:error];
}

问题是没有任何东西可以保存。我有一个 managedObjectContext(主线程),它有一个 privateWriterManagedObjectContext 的父级(连接到 PSC)。当我导入东西时,我将创建一个 importContext(一些后台线程),它有一个 managedObjectContext 的父级。当我获得新数据并尝试保存流程时,将是这样的。

(记住验证是检查对象的 privateWriterMOC)

在 importContext 上创建对象 -> 保存 -> 验证 -> 好的。

(数据被推送到 importContext 的父对象 managedObjectContext)。

保存 managedObjectedContext -> 验证 -> 好的。

(数据被推送到 managedObjectContext 的父级 privateWriterMOC)。

保存 privateWriterMOC -> 验证 -> 失败。privateWriterMOC 识别出对象在其上下文中并且不会保存它们。

似乎没有很多关于使用 validateForInsert 的文档,所以我希望有人对如何执行此操作有建议?

4

1 回答 1

1

这是我用于生产的代码:

- (BOOL)validateForInsert:(NSError *__autoreleasing *)error {
    [[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator] lock];
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([Deal class])];
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"identifier == %@", self.identifier];
    NSError *validateError;
    int count = [[(AppDelegate *)[[UIApplication sharedApplication] delegate] validationContext] countForFetchRequest:fetchRequest error:&validateError];
    if (count > 0) {
        [[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator] unlock];
        return FALSE;
    }
    [[(CWAppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator] unlock];
    return [super validateForInsert:error];
}

锁定 PSC 是这项工作的关键。在不同的上下文尝试同时联系 PSC 之前,我遇到了很多死锁。我发现这种方法的唯一缺陷是,如果一个对象返回 false,则整个上下文将被标记为无效并且不会保存。假设您有许多好对象,但一个坏对象都在未保存的上下文中,那么单个坏对象将不允许保存好对象。我在测试时遇到了这个问题,但在生产中我没有看到任何问题。


我将发布一个我在编写问题并进行测试时提出的答案。我不知道这是否是正确的想法,但目前似乎正在防止重复。

我所做的是在我的 appDelegate 中创建另一个名为 validationContext 的上下文,并将其设置为与 privateWriterMOC 完全相同。基本上这个想法是它只连接到商店,并且它知道的唯一数据已经被写入。当调用 validateForInsert 时,我使用了 validationContext 来执行获取,如果该对象已被保存,它将通知我。

我相信竞争条件仍然可能发生(假设写入和获取同时发生并且获取首先返回)但我会更多地研究它(也许 sqlite3 数据库是原子的?)。竞争条件是一个非常极端的情况,但我只是想处理它以防万一。

于 2012-12-20T22:47:32.323 回答