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

Posted

技术标签:

【中文标题】核心数据:NSManagedObjectContext 未保存/获取请求失败,直到应用退出和重新启动【英文标题】:Core Data: NSManagedObjectContext Not Saved / Fetch Request Fails Until App Quits & Relaunches 【发布时间】:2013-05-05 22:56:11 【问题描述】:

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

如果我通过按主页按钮“退出”应用程序,然后重新打开应用程序。它开始按预期运行(在适当的情况下返回结果)。我还可以删除该应用程序,然后从 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 个条目,但它应该在尝试插入第二个条目时看到第一个条目。

【问题讨论】:

【参考方案1】:

我猜你的self.managedObjectContext 有一个父上下文。

如果是这种情况,为了保证属性的唯一性,你将不得不一直保存到store(递归保存直到没有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;

【讨论】:

是的,这是我的问题,我认为这与这样的线程有关,但不确定如何解决。此外,为了避免编译器抱怨,我必须在块中声明并实例化一个新的 NSError 变量,如下所示:NSError *blockError = Nil;(我也尝试将此作为编辑添加到您的帖子中,但它被拒绝了:-() 不需要额外的error 对象,只需将您拥有的对象声明为__block NSError* error = nil;。很乐意提供帮助 是的,我尝试将它设置为 Nil,它也可以工作......但我宁愿记录错误以防万一出现问题。

以上是关于核心数据:NSManagedObjectContext 未保存/获取请求失败,直到应用退出和重新启动的主要内容,如果未能解决你的问题,请参考以下文章

MagicalRecord MR_saveToPersistentStoreWithCompletion 极慢

这个 NSFetchRequest 有啥问题?

在 swift 3 中获取相关表

如何通过 Interface Builder 的 XIB 传递 NSManagedObjectContext

无法将表达式的类型“NSDictionary”转换为类型“StringLiteralConvertible”

大数据核心技术都有哪些