KVO 和 Core Data - 自观察托管对象
Posted
技术标签:
【中文标题】KVO 和 Core Data - 自观察托管对象【英文标题】:KVO and Core Data - Self observing managed object 【发布时间】:2015-06-22 09:07:38 【问题描述】:我认为这个问题很简单也很常见,但我仍然不明白为什么它不起作用。让我暴露上下文:
假设我有一个不错的核心数据模型,其中包含一个名为 Document 的实体。该文档有类型、日期、编号和版本...例如,类型:D,日期:17-10-2015,编号:24 和版本 3。 本文档具有使用这四个值计算的标识符:D20151017-24-R03。
会有很多这样的文档,我必须通过它的标识符来搜索它们,我也会使用很多NSFetchedResultsController
。所以暂时的可能性就出来了。
这就是我所做的。先注册观察四个相关属性:
- (instancetype)initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context
self = [super initWithEntity:entity insertIntoManagedObjectContext:context];
if (self)
[self addObserver:self forKeyPath:_Property(documentTypeRaw) options:0 context:KVODocumentIdContext];
[self addObserver:self forKeyPath:_Property(date) options:0 context:KVODocumentIdContext];
[self addObserver:self forKeyPath:_Property(number) options:0 context:KVODocumentIdContext];
[self addObserver:self forKeyPath:_Property(version) options:0 context:KVODocumentIdContext];
return self;
然后,在释放时注销:
- (void)dealloc
[self removeObserver:self forKeyPath:_Property(documentTypeRaw) context:KVODocumentIdContext];
[self removeObserver:self forKeyPath:_Property(date) context:KVODocumentIdContext];
[self removeObserver:self forKeyPath:_Property(number) context:KVODocumentIdContext];
[self removeObserver:self forKeyPath:_Property(version) context:KVODocumentIdContext];
最后,管理通知:
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
if (context == KVODocumentIdContext)
[self updateDocumentId];
else
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
就在这里updateDocumentId
:
- (void) updateDocumentId
NSString * prefix = [self documentTypePrefix:self.documentTypeRaw];
NSString * date = [self.date documentIdFormat];
NSString * number = [NSString stringWithFormat:@"%.2d",[self.number shortValue]];
NSString * version = [self.version isEqualToNumber:@0]?@"":[NSString stringWithFormat:@"-R%.2d",[self.version shortValue]];
self.documentId = [NSString stringWithFormat:@"%@%@-%@%@",prefix,date,number,version];
对我来说,这应该是完美的......但是......它没有......
我有一个很好的:
failed: caught "NSInternalInconsistencyException", "<MBSDocument: 0x7fd9dbb45f40> (entity: MBSDocument; id: 0x7fd9dbb3cd00 <x-coredata:///MBSDocument/tB55CB581-AEC0-4211-A78A-7C48377BACC2612> ; data:
...
An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: date
Observed object: <MBSDocument: 0x7fd9dbb45f40> (entity: MBSDocument; id: 0x7fd9dbb3cd00 <x-coredata:///MBSDocument/tB55CB581-AEC0-4211-A78A-7C48377BACC2612> ; data:
...
我尝试了很多方法,其中包括在observeValueForKeyPath:ofObject:change:context:
中删除对super
的调用,或者在init
中注册等。但没有任何效果。好吧,我们将不胜感激。
提前致谢。
编辑:这是定义上下文的方式:
static void * KVODocumentIdContext = &KVODocumentIdContext;
编辑2:文档类继承自NSManagedObject
。
【问题讨论】:
你是如何使用上下文进行观察的。观察环境似乎有所不同。 根据nshipster.com/key-value-observing/#correct-context-declarations我以同样的方式声明了上下文(我已经编辑了帖子) 【参考方案1】:第一件事:我不会覆盖 initWithEntity:
这是 Apple 官方 API 文档中 NSManagedObject 类的节选:
“您也不鼓励重写 initWithEntity:insertIntoManagedObjectContext: 或 dealloc。更改 initWithEntity:insertIntoManagedObjectContext: 方法中的值不会被上下文注意到,如果您不小心,这些更改可能不会被保存。大多数初始化应在其中一种唤醒…方法中执行自定义。”
因此,您可能应该在 awakeFromInsert: 或 awakeFromFetch: 中添加这些 KVO 观察结果:(然后在 didTurnIntoFault 中删除这些观察者)您的子类的重写方法,也许您可以免除添加和删除观察者的所有这些开销,具体取决于什么会影响您的计算属性。
如果影响计算属性的键路径不是很多关系,那么您不妨编写您的 documentID 计算属性 getter 访问器并实现类方法 +(NSSet *)keYPathsForValiesAffectingDocumentID,它返回一个包含键路径的 NSSet,如果更改将导致使用新值重新计算计算机属性。
【讨论】:
【参考方案2】:KVODocumentIdContext 是问题区域,请详细说明和[超级观察..]方法
【讨论】:
我编辑了这篇文章来解释我是如何定义上下文的。 (我是根据nshipster.com/key-value-observing/#correct-context-declarations编辑的) 意思是同时删除上下文并检查是否一切正常?也分享班级等级制度 该类只是继承自NSManagedObject
,这里没什么特别的...但是如果我删除上下文,我怎么知道我不会弄乱NSManagedObject
的内置KVO?
为什么需要调用 [super observeValue..]?
我认为不需要调用 'super' 并更仔细地使用上下文或删除它,你应该没问题【参考方案3】:
[self addObserver:self
(如self.delegate = self
)应该已经敲响了警钟!您永远不需要观察自己的属性,您可以自定义设置器。
创建setter,调用动态超级方法(由NSManagedObject
自动添加),然后进行自定义工作,例如
@interface Event (DynamicAccessors)
- (void)managedObjectOriginal_setTimestamp:(NSDate *)timestamp;
@end
@implementation Event
- (void)setTimestamp:(NSDate *)timestamp
[self managedObjectOriginal_setTimestamp:timestamp];
// custom action for when the timestamp has been changed.
@end
因此,您将创建 4 个自定义设置器并从它们中调用 updateDocumentId
。
【讨论】:
以上是关于KVO 和 Core Data - 自观察托管对象的主要内容,如果未能解决你的问题,请参考以下文章
NotificationCenter KVC KVO Delegate总结