Core Data 不会将属性更新保留到数据库

Posted

技术标签:

【中文标题】Core Data 不会将属性更新保留到数据库【英文标题】:Core Data Wont Persist Property Updates to Database 【发布时间】:2011-04-12 00:15:40 【问题描述】:

我正在使用 NSManagedObject 的子类。实际上,它继承自一个类,该类继承自一个本身继承自 NSManagedObject 的类(这应该不是问题,对吧?)。

问题

在我对对象的属性进行更改后,对象会在其生命周期内记住更改,但不会将更改保存到数据库中。

我怎么知道的?

我知道这是因为:

当我重新启动应用程序时,我所做的更改会丢失。 告诉上下文刷新对象——在我对对象进行更改并告诉上下文保存之后——将对象的值设置回我进行更改之前的原始状态。 在模拟器中运行应用程序时,我可以在 Finder 中查看 sqlite 数据库文件,当我尝试保存上下文时,它的修改日期没有更新。

没有任何东西被写入数据库!

上下文

我正在使用自动生成的委托方法来创建商店协调器和上下文。然后,按照文档中的建议,我在它们的 init 方法中将上下文传递给视图控制器。商店是 SQLite。

我能够成功地将对象插入数据库并读取它们。我什至可以对新插入的对象进行属性更改并成功保存。当对象从数据库中拉出时,我似乎无法更新对象属性。

对象是通过与另一个对象的关系从存储中获取的。在对其属性进行更改后,我调用上下文的保存方法。但是,在这样做之前,我调用了对象的 isUpdated 方法和上下文的 hasChanges 方法,两者都返回 false。由于我刚刚更改了对象的属性但没有保存上下文,它们不应该返回 true 吗?

更多

如果我在保存上下文之前调用对象的committedChanges 方法,但是,传入我已更改的属性的名称,我会取回属性的正确值。我不确定这意味着什么。我本来以为这意味着对象的新属性值已经成功保存,但显然它们没有保存。

我知道结果对象是在上下文中注册的。如果我打电话

[[result managedObjectContext] refreshObject:result mergeChanges:YES];

对象恢复为原始属性值。这意味着上下文存在并且它与从中获取记录的上下文相同。这意味着新的属性值永远不会写入数据库。

一些代码

这是我正在研究所有这些东西的代码。在我的代码中还有其他地方我正在更改属性,但这些更改永远不会保存。

- (IBAction)statusControlChanged:(UISegmentedControl *)control 
WCAAssessmentResult *result = [self currentResult];

    /* printing the existing property values */
    if (![result.complete boolValue]) NSLog(@"result is in progress!");
    else if ([result.passed boolValue]) NSLog(@"result is passed!");
    else NSLog(@"result is not passed!");

    /* changing the property values */
    switch (control.selectedSegmentIndex) 
        case 0:
            NSLog(@"setting incomplete");
            result.complete = [NSNumber numberWithBool:NO];
            break;
        case 1:
            NSLog(@"setting passed");
            result.passed = [NSNumber numberWithBool:YES];
            result.complete = [NSNumber numberWithBool:YES];
            break;
        case 2:
            NSLog(@"setting failed");
            result.passed = [NSNumber numberWithBool:NO];
            result.complete = [NSNumber numberWithBool:YES];
            break;
        default:
            break;
    

    /* this method always returns an empty dictionary */
    NSLog(@"%@", [result changedValues]);

    /* this method returns the values that I just set */
    NSLog(@"%@", [result committedValuesForKeys:[NSArray arrayWithObjects:@"complete", @"passed", nil]]);

    /* isUpdated returns false */
    if (![result isUpdated]) 
        NSLog(@"result is not updated?! WTF!?!?");
    

    /* hasChanges returns false */
    if (![[result managedObjectContext] hasChanges]) 
        NSLog(@"context has no changes!? WTF!?!?");
    

    /* saving the context produces no error */
    NSError *error = nil;
    if (![[result managedObjectContext] save:&error]) 
        NSLog(@"save failed");
        NSLog(@"%@",[error description]);
    

转折

如果我通过在上下文中插入一条新记录来创建一个新的结果对象,我可以设置该对象的属性并成功保存它们。在上面的代码中,我从另一个对象获取对象作为一对多关联的成员。这是线索吗?

我正在为此撕毁我的头发。这里到底出了什么问题?

什么不是问题

我已经记录了对象的类,它确实是正确的类 我已确保我保存的 managedObjectContext 与对象的上下文相同 我没有对托管对象类的自动生成的 setter/getter 方法进行任何更改 我尝试过使用 setValue:forKey: 方法而不是对象的属性 我使用-com.apple.CoreData.SQLDebug 1 参数记录Core Data SQL,当我更新和保存对象的属性时没有记录SQL

【问题讨论】:

常见问题?从来没有听说过。在里面放一个NSAssert(result, @"TF!"); 同意 - result nil 首先?也将一个 NSError 对象传递给save: 并询问其结果? 更新了问题。错误对象为空。 它应该为自己感到羞耻。 您是否继承了NSManagedObject?如果是这样,您可以发布该代码吗? 【参考方案1】:

我不太明白你的说法

WCAAssessmentResult *result = [self currentResult];

确实,如果您从一个对象访问一对多关系,您应该返回一个集合,而不是一个对象。无论如何,没有看到代码很难说。您遇到的问题可能存在也可能不存在。

我宁愿在您的代码中使用类似于以下 sn-p 的内容来访问属于一对多关系的对象。我假设yourObject 是您用来访问多对多关系中的WCAAssessmentResult 对象的对象,我称之为results

NSMutableSet *resultObjects = [yourObject mutableSetValueForKey:@"results"];
NSPredicate *predicate = ...
[resultObjects filterUsingPredicate:predicate];

for(WCAAssessmentResult *result in resultObjects)

   // modify as needed the current result object



NSError *error = nil;
if (![managedObjectContext save:&error]) 
        NSLog(@"save failed");
        NSLog(@"%@",[error description]);

您是否验证了用于保存对象的 managedObjectContext 是否有效(不是 nil)?

【讨论】:

嗨!感谢您的回答。 currentResult 函数只是从视图控制器的数组实例变量中返回 WCAAssessmentResult 对象之一。该数组通过视图控制器的 init 方法传入,并且数组中的对象存在且有效。我已经测试过结果对象的 managedObjectContext 是正确的。我什至检查了它的 persistentStoreCoordinator 的存储路径,它是正确的数据库文件,当我保存上下文时应该更新它……但它没有更新! 您好,但话又说回来,您必须更好地解释一下:鉴于您要修改的对象是一对多关系中的对象,您如何获得放入的对象你传递给控制器​​的数组?访问这些对象的正确方法如我的代码所示。 Core Data 实际上为一对多关系返回一组对象:您是否使用该集合创建一个数组然后保留该数组? 感谢您的坚持(双关语!)。我正在使用自定义 NSManagedObject 子类,因此我使用属性名称访问一组值。我通过在集合上调用 allObjects 来创建数组。对象都在那里。我可以读取它们的值并在内存中更改它们。当我保存上下文时,这些值没有被保存。 好的。假设您的自定义类通过在模型中显式设置的父类正确反映在您的模型中,我想知道您是实现了自定义验证还是使用了默认验证。我怀疑问题可能与验证过程有关,可能是您没有设置一些非可选属性或类似的东西。您是否已经验证是否确实如此? 我没有进行任何自定义验证。不过,我会看看这个问题并报告回来。谢谢!【参考方案2】:

一些想法没有特别的顺序:

我会记录result 对象的类并确保它是您认为的类。与超类/子类的一些混淆可能会导致某些值不被保存。

如果您对层次结构中任何类的 setter/getter 方法进行了任何更改,请仔细查看这些方法,尤其是如果您使用了 primativeValue 方法。简单地省略willChangeValuedidChangeValue 可能会导致更改对上下文不可见,有时对对象本身不可见。

我会记录您正在保存的上下文以及result 对象的managedObjectContext 属性。确认它们确实是相同的上下文。

跳过使用属性访问器(点表示法)并使用setValue:forKey,看看是否有什么不同。如果是这样,您有访问器问题。同样,您也可以尝试setPrimativeValue:forKey 来检查访问器问题。

如果我必须打赌,我会把钱花在你将 fetch 返回的对象分配给错误的类上。

【讨论】:

我已经浏览了这些(我已经有一些)并更新了我的问题以反映它。还没有运气。不过谢谢! 我现在没有想法。这真是一个奇怪的问题。您可以尝试发布项目文件的链接(如果可能),看看是否有人能发现一些东西。这不是一个普通的问题,我认为任何人都无法通过二手描述发现问题。

以上是关于Core Data 不会将属性更新保留到数据库的主要内容,如果未能解决你的问题,请参考以下文章

策略问题:从 XML 检索数据以更新本地 Core Data DB?

Core Data 自动迁移不会在我的新模型中创建新实体

Core Data 未更新可转换属性

Core Data 在 fetchObjects 中保留两个对象而不是 27 个

在使用 NSFetchedResultsController 更新 UITableView 时将来自 Web 的数据保存到 Core Data

以编程方式将属性添加到 Core Data