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

时间:2015-06-22 09:07:39

标签: ios core-data ios8 nsmanagedobject key-value-observing

我认为这个问题非常简单和常见,但我仍然不明白为什么它不起作用。让我揭露背景:

让我说我有一个很好的核心数据模型,其中有一个名为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:
...

我尝试过很多事情,其中​​包括取消superobserveValueForKeyPath:ofObject:change:context:的电话,或init中的注册等。但没有任何效果。好吧,非常感谢一些帮助。

提前致谢。

编辑:这是定义上下文的方式:

static void * KVODocumentIdContext = &KVODocumentIdContext;

编辑2 :文档类继承自NSManagedObject

3 个答案:

答案 0 :(得分:1)

首先要做的是:我不会覆盖initWithEntity:

这是Apple针对NSManagedObject类的官方API文档的摘录:

“您也不鼓励覆盖initWithEntity:insertIntoManagedObjectContext:或dealloc。更改initWithEntity:insertIntoManagedObjectContext:方法中的值将不会被上下文注意到,如果您不小心,可能无法保存这些更改。应该在一个清醒......方法中执行自定义。“

所以在给你的时候,你应该在awakeFromInsert:或awakeFromFetch :(然后在didTurnIntoFault中删除这些观察者)覆盖你的子类的方法中添加这些KVO观察,也许你可以免除所有这些增加和删除观察者的开销,这取决于什么会影响你的计算属性。

如果影响计算属性的keypath不是很多关系,那么你也可以编写你的documentID计算属性getter访问器并实现类方法+(NSSet *)keYPathsForValiesAffectingDocumentID,它返回一个包含keypath的NSSet。更改将导致使用新值重新计算计算机属性。

答案 1 :(得分:0)

KVODocumentIdContext是问题区域请详细说明和[超级观察..]方法

答案 2 :(得分:0)

[self addObserver:self (如self.delegate = self)应该关闭了闹钟!您无需观察自己的属性,即可创建自定义设置器。

创建设置器,调用动态超级方法(由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