核心数据不会将属性更新保留到数据库
我正在使用 NSManagedObject 的子类。实际上,它继承自一个类,该类又继承自一个本身又继承自 NSManagedObject 的类(这应该不是问题,对吧?)。
问题
在我对对象的属性进行更改后,该对象会在其生命周期中记住这些更改,但这些更改永远不会保存到数据库中。
我怎么知道这一点?
我知道这一点是因为:
- 当我重新启动应用程序时,我所做的更改将会丢失。
- 告诉上下文刷新对象 - 在我对对象进行更改并告诉上下文保存之后 - 将对象的值设置回我进行更改之前的原始状态。
- 在模拟器中运行应用程序时,我可以在 Finder 中查看 sqlite 数据库文件,并且当我尝试保存上下文时,它的修改日期不会更新。
没有任何内容写入数据库!
上下文
我正在使用自动生成的委托方法来创建存储协调器和上下文。然后,我按照文档中的建议,将上下文传递给视图控制器的 init 方法。该商店是 SQLite。
我能够成功地将对象插入数据库并读取它们。我什至可以对新插入的对象进行属性更改并成功保存。当对象从数据库中拉出时,我似乎无法更新对象属性。
该对象是通过另一个对象的关系从存储中获取的。更改其属性后,我调用上下文的 save 方法。然而,在这样做之前,我调用了对象的 isUpdated 方法和上下文的 hasChanges 方法,两者都返回 false。因为我刚刚更改了对象的属性但尚未保存上下文,所以它们不应该返回 true 吗?
更多
如果我在保存上下文之前调用对象的commitedChanges 方法,但传入我已更改的属性名称,我将返回属性的正确值。我不确定这意味着什么。我本以为这意味着对象的新属性值已成功保存,但显然它们没有保存。
我知道结果对象是在上下文中注册的。如果我调用
[[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
I'm working with a subclass of NSManagedObject. Actually, it inherits from a class that inherits from a class that itself inherits from NSManagedObject (that shouldn't be a problem, right?).
The problem
After I make changes to the properties of the object, the object remembers the changes for its lifetime, but the changes are never saved to the database.
How Do I Know This?
I know this because:
- when I restart the app, the changes I've made are lost.
- telling the context to refresh the object – AFTER I've made changes to the object and told the context to save – sets the object's values back to their original state before I made the changes.
- when running the app in the simulator, I can look at the sqlite database file in the Finder, and it's modified date isn't updated when I attempt to save the context.
Nothing is being written to the database!
Context
I'm using the auto-generated delegate methods to create the store coordinator and the context. Then I'm passing the context to the view controllers in their init methods, as recommended in the docs. The store is SQLite.
I am able to successfully insert objects into the database and read them. I can even make property changes to the newly inserted object and save it successfully. I simply don't seem to be able to update object properties when the object is pulled back out of the database.
The object is fetched from the store via a relationship from another object. After making changes to its properties, I call the context's save method. However, before doing so, I call the object's isUpdated method and the context's hasChanges method, and both return false. Shouldn't they return true since I've just made changes to the object's properties but haven't saved the context?
More
If I call the object's committedChanges method before saving the context, however, passing in the names of the properties that I've changed, I get back the correct values of the properties. I'm not sure what this means. I would have thought that this means that the object's new property values have been successfully saved, but clearly they are not saved.
I know that the result objects is registered with a context. If I call
[[result managedObjectContext] refreshObject:result mergeChanges:YES];
the object reverts back to the original property values. This means that the context is there and that it is the same context from which the record was fetched. And it means that the new property values are never written tot he database.
Some Code
Here's the code where I'm poking around with all of these things. There are other places in my code where I'm making property changes, but the changes are never saved.
- (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]);
}
}
A Twist
If I create a new result object by inserting a new record into the context, I can set that object's properties and they are saved successfully. In the above code, I'm fetching the object as a member of a to-many association from another object. Is that a clue?
I'm tearing my hair out over this. What the hell could be going wrong here?
What's NOT The Problem
- I've logged the object's class, and it is indeed the correct class
- I've made sure that the managedObjectContext I'm saving is the same as the object's context
- I haven't made any changes to the auto-generated setter/getter methods of my managed object classes
- I've tried using the setValue:forKey: method instead of object's properties
- I've used the
-com.apple.CoreData.SQLDebug 1
argument to log Core Data SQL, and no SQL is logged when I update and save the object's properties
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我不太明白你的说法
确实,如果你从一个对象访问一对多关系,你应该返回一个集合,而不是一个对象。无论如何,在没有看到代码的情况下很难判断。您遇到的问题可能存在,也可能不存在。
我宁愿期望在您的代码中使用类似以下代码片段的内容来访问属于一对多关系的对象。我假设
yourObject
是您用来访问对多关系中的WCAAssessmentResult
对象的对象,我将其称为results
。您是否验证用于保存对象的 ManagedObjectContext 是否有效(不是 nil)?
I do not really understand your statement
Indeed, if you are accessing a to-many relationship from an object, you should get back a set, not an object. Anyway, without seeing the code it's hard to tell. The problem you are experiencing may or may not lie there.
I would rather expect in your code something like the following snippet to access objects belonging to a to-many relationship. I assume that
yourObject
is the object you use to access theWCAAssessmentResult
objects in the to-many relationship, which I callresults
.Did you verify that the managedObjectContext you are using to save the object is valid (not nil) ?
一些想法(排名不分先后):
我将记录
result
对象的类,并确保它是您认为的类。与超类/子类的一些混淆可能会导致某些值无法保存。如果您对层次结构中任何类的 setter/getter 方法进行了任何更改,请仔细查看这些方法,尤其是在您使用
primativeValue
方法时。简单地省略willChangeValue
和didChangeValue
可能会导致更改对上下文不可见,有时对对象本身不可见。我将记录您正在保存的上下文以及
result
对象的managementObjectContext
属性。确认它们确实是相同的上下文。跳过使用属性访问器(点符号)并使用 setValue:forKey 并查看是否有任何区别。如果是这样,则存在访问器问题。同样,您也可以尝试 setPrimativeValue:forKey 来检查访问器问题。
如果我必须打赌,我会把钱押在你将 fetch 返回的对象分配给错误的类上。
Some ideas in no particular order:
I would log the class of the
result
object and make sure it is the class you think it is. Some confusion with super/sub classes could result in certain values not being saved.If you made any alterations in the setter/getter methods in any class in the hierarchy, look closely at those methods especially if you used a
primativeValue
method. Simply leaving outwillChangeValue
anddidChangeValue
can cause changes to be invisible to the context and sometimes to the object itself.I would log the context you are saving as well as
managedObjectContext
property of theresult
object. Confirm they are indeed the same context.Skip using the property accessors (the dot notation) and use
setValue:forKey
and see if that makes any difference. If so, you have an accessor problem. Likewise, you could trysetPrimativeValue:forKey
to also check for an accessor problem.If I had to bet, I would put my money on you assigning the objects returned by a fetch to the wrong class.