自动更新 NSManagedObject 属性修改时间戳

Posted

技术标签:

【中文标题】自动更新 NSManagedObject 属性修改时间戳【英文标题】:Autoupdating NSManagedObject property modification timestamp 【发布时间】:2011-12-28 22:28:30 【问题描述】:

我有一个NSManagedObject 有两个属性:

NSNumber *score;
NSDate *score_timestamp;

我希望每次更新 score 时都更新我的 score_timestamp 字段。

我显然不能使用-willSave 方法,因为我的上下文偶尔会被保存,而score_timestamp 不会是最新的。所以我应该覆盖-setScore: 或者将我的托管对象设置为它自己的score 字段的键值观察器。

-setScore: 的解决方案似乎很简单:

- (void) setScore:(NSNumber *)score

    [self willChangeValueForKey:@"score"];
    [self setPrimitiveScore:score];
    [self didChangeValueForKey:@"score"];

    self.score_timestamp = [NSDate date];

以这种方式做事有什么注意事项吗?还是我应该使用 KVO 解决方案?

更新

到目前为止,我收到了两个回复,我的代码无法通过 setValue: forKey: 运行,我仍在等待例如。天真地调用[(NSManagedObject *)myObject setValue:value forKey:@"score"] 调用我的设置器都是一样的。

所以如果我切换到 KVO 解决方案,我是否应该在所有 awake 方法中 addObserver: 并在 willTurnIntoFault 中删除它?还是没那么简单?

【问题讨论】:

说真的,两个回答者都说 setValue... 不起作用是错误的。您可以通过在访问器中放置一个断点,然后使用 setValue 更改值来非常简单地进行测试。 @jrturton 我已经测试过了,它肯定会被调用。但我不是核心数据专家,无法确定是否有另一种方法可以在没有访问器的情况下更改我的属性。我的意思是在实际代码中偶尔使用的方式。 不。所有核心数据调用都将通过您的设置器(可能首先通过 setValueForKey)。这是封装原则的一部分。您可以对 ivar 进行一些怪异的直接访问,但这必须是您自己编写的,这可能也会破坏托管对象上下文。你不会这样对自己,对吗? @jrturton 是的,这是一个很好的解释!我从一开始就是这么想的(否则我会立即从 KVO 开始),但是你看,有些人说这不正确 :) 如果你创建一个答案让我标记它会很棒。 @jrturton 抱歉,我好像注意力不集中,我肯定需要睡觉了。我刚刚发现我在你的合法答案中添加了一个警告(错过了你编辑它的那一刻),所以我删除了我的笔记。让我们按照以下方式来做吧:我现在不能相信自己做出决定,所以明天早上我会回到我的问题 :) 谢谢你的帮助! 【参考方案1】:

您问题中的实现很好。任何 KVC 尝试更新您的值也将通过 setter 方法(setValue: forKey: 只是搜索匹配 setKey 的访问器方法,有关详细信息,请参阅 here)。

【讨论】:

我在NSManagedObject -didChangeValue: 文档中看到“您不得覆盖此方法”! 我已经编辑了您的答案,因为它开始获得 +1,同时很危险。感谢您为帮助我所做的努力,表达我的感激之情 我认为它在以前的版本中没有得到任何支持,但足够公平。这是一个有效的编辑! @iHunter:你刚刚添加了警告,now you're proposing to remove it?这里发生了什么?如果警告有效,它应该留在那里。 我重做了我的编辑,因为你更新了你的答案,它变得无关紧要。【参考方案2】:

您正在寻找Key-Value Observing

[objectWithArray addObserver:self 
                  forKeyPath:@"score"
                     options:NSKeyValueObservingOptionNew 
                     context:nil];

然后观察它:

-(void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary *)change 
                       context:(void *)context 

    //Check if [change objectForKey:NSKeyValueChangeNewKey] is equal to "score" 
    //and update the score_timestamp appropriately

我相信,当您从获取中醒来时应该注册通知并在您出错时取消注册。

【讨论】:

是的,KVO 在这里是一个很好的解决方案,但我在现有代码中没有发现问题。我怀疑在 setter 中设置另一个属性不是一个好主意,但我想知道为什么,因为我认为它有效 :) 如果你能保证你的代码永远只使用动态生成的 getter/setter 这个属性,你的代码应该可以工作,但是如果你曾经使用 setValue:forKey: 时间戳将不会已更新。 @iHunter - 在 setter 中设置另一个属性没有错。与在任何其他代码中设置它没有什么不同。如果你设置 same 属性,你会遇到麻烦,但这里不是这样。 并且在NSManagedObject 中使用KVO 来观察self 需要在所有awake/willTurnIntoFault 方法中准确添加/删除。还是完全没有? @AshFurrow - 事实并非如此。 setValue: forKey: 只搜索正确命名的访问器方法并调用它,如我的答案中链接的文档中所述。【参考方案3】:

在此方法中,如果出于任何原因您将对象修改为不是自定义子类,而是使用setValue: forKey: 修改为NSManagedObject,则不会更新日期。

【讨论】:

您能举个例子吗?

以上是关于自动更新 NSManagedObject 属性修改时间戳的主要内容,如果未能解决你的问题,请参考以下文章

在具有 VIPER 架构的应用程序上更新 NSManagedObject

获取核心数据中 NSManagedObject 的修改日期?

在更新时跟踪 NSManagedObject 属性并立即更新视图

willSave 或 validateForUpdate 中的 NSManagedObject 动态属性更新

NSManagedObject 时间戳更新

自动生成的 NSManagedObject 中的属性