Why do I get an uncaught exception when implementing my own KVC setter / getter methods

My model classes are mostly implemented using synthesized setter / getter methods, and everything was fine. Everything was well connected to the user interface. Later I realized that changing one property should lead to changing other properties (changing type can lead to changes in minA and maxA ), so I manually wrote the setter / getter methods for the type property. Code follows:

QBElementType

 @interface QBElementType : NSObject @property NSRange minRange; @property NSRange maxRange; @end @implementation QBElementType @synthesize minRange; @synthesize maxRange; @end 

QBElement

 @interface QBElement : QBElementType{ QBElementType *_type; } @property (weak) QBElementType *type; @property NSUInteger minA; @property NSUInteger maxA; @end @implementation QBElement @synthesize minA = _minA; @synthesize maxA = _maxA; - (QBElementType*)type { return _type; } - (void)setType:(QBElementType*)newType { [self willChangeValueForKey:@"type"]; _type = newType; // no need to bother with retain/release due to ARC. [self didChangeValueForKey:@"type"]; /* Having the following 4 lines commented out or not changes nothing to the error if (!NSLocationInRange(_minA, newType.minRange)) self.minA = newType.minRange.location; if (!NSLocationInRange(_maxA, newType.maxRange)) self.maxA = newType.maxRange.location; */ } @end 

QUESTION Since when I change the type of an element, I get an uncaught exception:

Unable to update for observer <NSTableBinder ...> for the key path "type.minRange" from <QBElement ...>, most likely because the value for the key "type" was changed without a corresponding KVO notification sent. Verify that the KVO matches the QBElement class.

Is there something obvious I'm missing from KVO-Compliance ? Why am I getting this error?

+6
source share
3 answers

You do not need to explicitly call -willChangeValueForKey: and -didChangeValueForKey: because your installer is named correctly. Calls to them will be automatically added through the magic of KVC / KVO machines. The problem may be that they are actually called twice.

In addition, since it turns out that minA and maxA simply derived from this type, you can readonly them and tell KVO to automatically notify observers that minA and maxA have changed at any time type :

 @interface QBElement : QBElementType{ QBElementType *_type; } @property (weak) QBElementType *type; @property (readonly) NSUInteger minA; @property (readonly) NSUInteger maxA; @end @implementation QBElement + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; if ([key isEqualToString:@"minA"] || [key isEqualToString:@"maxA"]) { keyPaths = [keyPaths setByAddingObject:@"type"]; } return keyPaths; } @synthesize type; - (NSUInteger)minA { return self.type.minRange.location; } - (NSUInteger)maxA { return self.type.maxRange.location; } @end 
+3
source

Implementing a manual setter does not mean that you definitely need to enter manual KVO notifications. You absolutely need this if you are updating the base ivar for the property without going through the setter. KVO will automatically replace your setters with versions that call the will / didChange methods before and after calling the "real" setter. If you want to call them yourself in your network device (for example, you need more precise control when calling them), you need to override the +automaticallyNotifiesObserversForKey: method to return NO for your type .

I assume that the reason you get the error right now is because the KVO machine sees that you called [self willChangeValueForKey:@"type"] twice in a row (once through KVO magic, once manually) without calling [self didChangeValueForKey:@"type"] .

+2
source

As others noted, you can turn off calls to willChangeValueForKey: didChangeValueForKey: and execute:

 + (NSSet *) keyPathsForValuesAffectingMinA { return [NSSet setWithObject:@"type"]; } 

The same goes for minB , of course.

Then create the minB only minA and minB properties.

Various ways to do this are mostly stylistic preferences.

0
source

Source: https://habr.com/ru/post/908372/


All Articles