Core Data: Emitting KVO notifications for transient, derived properties

匿名 (未验证) 提交于 2019-12-03 03:04:01

问题:

I have Parent entity with a custom class that has a transient and derived (read only) property called DerivedProperty.

The value of DerivedProperty is dependent on the value of Parent.IndependentProperty1 and so whenever IndependentProperty1 changes, the value of DerivedProperty will change. However, Parent has a to-many relationship to Child (called children) and DerivedProperty is also dependent on the value of IndependentProperty2 in all of Parent's Child objects.

So whenever IndependentProperty1 of Parent or IndependentProperty2 of any of the Child objects changes, I would like to notify any observers that DerivedProperty has also changed.

I've arrived at the following code so far. The only problem is that no KVO notifications are emitted for DerivedProperty since if I try to do this in objectContextDidChange: then the code will end up in a loop.

- (void) awakeFromInsert {     [super awakeFromInsert];      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(objectContextDidChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:self.managedObjectContext]; }   - (void) awakeFromFetch {     [super awakeFromFetch];      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(objectContextDidChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:self.managedObjectContext]; }  - (void) objectContextDidChange: (NSNotification *) notification {     NSSet *updatedObjects = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];      if ([updatedObjects containsObject:self] || [updatedObjects intersectsSet:self.children])     {         //clear caches         _derivedProperty  = nil;     } }  - (void) didTurnIntoFault {     [[NSNotificationCenter defaultCenter] removeObserver:self]; }  - (NSString *) DerivedProperty {     if (_derivedProperty == nil)     {         _derivedProperty  = [self someExpensiveCalculation];     }     return _derivedProperty  ; } 

I'm sure I need a total rethink on my approach here. I've tried using KVO to observe IndependentProperty1 and IndependentProperty2 of the to-many relation but I just can't seem to get it working right. If the derived property wasn't dependent on a to-many relationship then I'm sure I could just use keyPathsForValuesAffectingValueForKey: but of course that won't work across a to-many relationship.

How to I get KVO notifications working with a Core Data transient, derived property that is dependent on a to-many relationship?

回答1:

First, you are accessing the ivar for DerivedProperty in your -objectContextDidChange: method and that short circuits KVO notifications. You should really re-implement the property internally as read/write and use the generator accessor.

Second, subclasses of NSManagedObject have automatic KVO turned off by default. This is part of the architecture of Core Data. Therefore you would want to fire the notifications yourself if you are not going to use an accessor:

- (void) objectContextDidChange: (NSNotification *) notification {     NSSet *updatedObjects = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];      if ([updatedObjects containsObject:self] || [updatedObjects intersectsSet:self.children]) {         //clear caches         [self willChangeValueForKey:@"derivedProperty"];         _derivedProperty  = nil;         [self didChangeValueForKey:@"derivedProperty"];     } } 

Note

In this case the OP was using a transient property AND accessing an iVar for the property directly. This effectively created two iVars, one maintained by Core Data and another maintained by the OP's code. This was causing a collision.

If you are using a transient property it is recommended that you move calculations to another method as suggested and leave the accessors to Core Data.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!