Ios KVO和核心数据-自观察托管对象

Ios KVO和核心数据-自观察托管对象,ios,core-data,ios8,nsmanagedobject,key-value-observing,Ios,Core Data,Ios8,Nsmanagedobject,Key Value Observing,我认为这个问题很简单也很常见,但我仍然不明白为什么它不起作用。让我来说明一下背景: 假设我有一个很好的核心数据模型,其中有一个名为Document的实体。此文档包含类型、日期、编号和版本。。。例如,键入:D,日期:17-10-2015,编号:24和版本3。 此文档具有使用这四个值计算的和标识符:D20151017-24-R03 将会有很多这样的文档,我必须通过它的标识符来搜索它们,我还将使用大量的nsfetchedresultscoontroller。所以暂时的可能性是正确的 这就是我所做的。观

我认为这个问题很简单也很常见,但我仍然不明白为什么它不起作用。让我来说明一下背景:

假设我有一个很好的核心数据模型,其中有一个名为Document的实体。此文档包含类型、日期、编号和版本。。。例如,键入:D,日期:17-10-2015,编号:24和版本3。 此文档具有使用这四个值计算的和标识符:D20151017-24-R03

将会有很多这样的文档,我必须通过它的标识符来搜索它们,我还将使用大量的
nsfetchedresultscoontroller
。所以暂时的可能性是正确的

这就是我所做的。观察四个相关属性的第一个寄存器:

- (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];
    }
}
就在这里,
更新的文档ID

- (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:
...

编辑2:文档类继承自
NSManagedObject

kVodocumentContext是问题所在。请详细说明这一点和[super observe..]method

第一件事:我不会用Entity重写initWithEntity:

这是NSManagedObject类苹果官方API文档的摘录:

还不鼓励您重写initWithEntity:insertIntoManagedObjectContext:,或dealloc。在initWithEntity:insertIntoManagedObjectContext:方法中更改值不会被上下文注意到,如果您不小心,这些更改可能不会被保存。大多数初始化自定义应在唤醒…方法之一中执行。“

因此,在给出了子类的awakeFromInsert:或awakeFromFetch:(然后在didturnintofult中删除这些观察器)重写方法之后,您可能应该在其中添加这些KVO观察值,也许您可以根据影响计算属性的因素,避免添加和删除观察器的所有开销


如果影响计算属性的键路径不是多对多的关系,那么您最好只编写documentID计算属性getter访问器并实现类方法+(NSSet*)KeyPathForvaliesFectingDocumentId,返回一个NSSet,其中包含如果更改将导致使用新值重新计算计算机属性的键路径。

[self addObserver:self
(如
self.delegate=self
)你不需要观察自己的属性,你可以自定义设置

创建setter,调用dynamic super方法(由
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

您如何使用上下文进行观察。观察上下文似乎有所不同。根据我以相同的方式声明上下文(我编辑了文章),我编辑了文章,以解释我如何定义上下文。(我根据编辑)意思是在删除上下文并检查是否正常的同时?也共享类HirEarchy该类刚从
NSManagedObject
继承,这里没有什么特别之处…但是如果我删除上下文,我怎么知道我不会弄乱
NSManagedObject
的内置KVO?为什么调用[super observeValue..]是必需的吗?我不认为需要调用“super”,更仔细地使用上下文,或者删除它,你应该会没事的
@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