核心数据:NSManagedObjectContext 未保存/获取请求失败,直到应用程序退出并重新启动



我有一个核心数据后端,它表现出一些奇怪的行为。当我第一次运行我的应用程序(在模拟器中(时,即使连续多次给定相同的项目,我的NSFetchRequest也永远找不到匹配项(它会在每个请求中返回"无结果",然后继续向数据库中插入重复的信息-我可以看到这种情况的发生,因为我有一个绑定到数据库的表视图(。

如果我按下主页按钮"退出"该应用程序,然后重新打开该应用程序。它开始按预期运行(在适当的情况下返回结果(。我也可以删除该应用程序,然后从Xcode再次运行它,将此过程重置为一开始。它似乎直到应用程序关闭才保存数据库,即使我在NSManagedObjectContext上调用save(返回true(。

这是怎么回事?我如何使这项工作达到预期效果?我想我只是没有为NSManagedObjectContext保存我的更改,但我该怎么做呢?

以下是从我的NSManagedObjectContext中获取/返回对象的函数:

+ (Mark *)markWithTWLInfo:(NSDictionary *)markDictionary
   inManagedObjectContext:(NSManagedObjectContext *)context
{
    Mark *mark = nil;
    NSLog(@"Starting Operation for Key: %@", [markDictionary[JS_MARK_ID] description]);
    // Build a fetch request to see if we can find this Mark in the database.
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Mark"];
    request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"title" ascending:YES]];
    request.predicate = [NSPredicate predicateWithFormat:@"idUnique = %@", [markDictionary[JS_MARK_ID] description]];
    // Execute the fetch
    NSError *error = nil;
    NSArray *matches = [context executeFetchRequest:request error:&error];
    // Check what happened in the fetch
    if (!matches || ([matches count] > 1) || error ) {  // nil means fetch failed; more than one impossible (unique!)
        // handle error
        if (error) {
            NSLog(@"Fetch error: %@", [error description]);
        } else {
            NSLog(@"Found No/Multiple matches for key: %@", [markDictionary[JS_MARK_ID] description]);
        }
    } else if (![matches count]) { // none found, so let's create a mark
        NSLog(@"Inserting: %@", [markDictionary[JS_MARK_ID] description]);
        mark = [NSEntityDescription insertNewObjectForEntityForName:@"Mark" inManagedObjectContext:context];
        mark.idUnique   = [NSNumber numberWithInt:[markDictionary[JS_MARK_ID] intValue]];
        //Save the changes; this returns True
        if ([context save:&error]) {
            NSLog(@"Saved is true");
        } else {
            NSLog(@"Saved is false");
        }
        if (error) {
            NSLog(@"Save error: %@", [error description]);
        }
    } else { // found the mark, just return it from the list of matches (which there will only be one of)
        NSLog(@"Found existing object for key: %@", [markDictionary[JS_MARK_ID] description]);
        mark = [matches lastObject];
    }
    return mark;
}

对于要插入的每个标记,我都这样调用此函数:

for (NSDictionary *mark in results) {
    if (DEMO_LOGGING) NSLog(@"Inserting: %@",[mark objectForKey:@"Mark"]);
    [self.managedObjectContext performBlock:^{
        [Mark markWithTWLInfo:[mark objectForKey:@"Mark"] inManagedObjectContext:self.managedObjectContext];
    }];
}

以下是我遇到此问题时在日志中看到的内容:

-使用新数据库启动应用程序:

2013-05-05 16:45:08.105 ToWatchList[10155:c07] Starting Operation for Key: 731
2013-05-05 16:45:08.106 ToWatchList[10155:c07] Inserting: 731
2013-05-05 16:45:08.111 ToWatchList[10155:c07] Saved is true
2013-05-05 16:45:10.651 ToWatchList[10155:c07] Starting Operation for Key: 731
2013-05-05 16:45:10.652 ToWatchList[10155:c07] Inserting: 731
2013-05-05 16:45:10.654 ToWatchList[10155:c07] Saved is true

-退出并在此处重新启动程序

2013-05-05 16:45:29.816 ToWatchList[10155:c07] Starting Operation for Key: 731
2013-05-05 16:45:29.817 ToWatchList[10155:c07] Found No/Multiple matches for key: 731

-现在,NSFetchRequest按预期返回了我之前的2个条目,但在尝试插入第二个条目时,它应该看到了第一个条目。

我的猜测是您的self.managedObjectContext有一个父上下文

如果是这种情况,为了确保属性的唯一性,您必须一直保存到存储区(递归保存,直到不存在parentContext为止(

请注意,在确保唯一性之前,必须等待所有父上下文的保存完成。

将您的[context save:&error]替换为:

NSManagedObjectContext* c = context;
__block BOOL success = YES;
while (c && success) {
    [c performBlockAndWait:^{
        success = [c save:&error];
        //handle save success/failure
    }];
    c = c.parentContext;
}

最新更新