Objective c 为什么在实现自己的KVC setter/getter方法时会出现未捕获的异常
我的模型类大部分是用合成的setter/getter方法实现的,一切都很好。一切都很好地连接到用户界面。后来我意识到更改一个属性会导致其他属性的更改(更改Objective c 为什么在实现自己的KVC setter/getter方法时会出现未捕获的异常,objective-c,macos,cocoa,key-value-observing,Objective C,Macos,Cocoa,Key Value Observing,我的模型类大部分是用合成的setter/getter方法实现的,一切都很好。一切都很好地连接到用户界面。后来我意识到更改一个属性会导致其他属性的更改(更改type可能会导致minA和maxA中的更改),因此我手动为type属性编写了setter/getter方法。代码如下: QBElementType @interface QBElementType : NSObject @property NSRange minRange; @property NSRange maxRange;
type
可能会导致minA
和maxA
中的更改),因此我手动为type
属性编写了setter/getter方法。代码如下:
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
问题:从那时起,每当我更改元素的类型时,都会得到一个未捕获的异常:
无法更新密钥路径的观察者
“type.minRange”从,很可能是因为
在没有适当的KVO通知的情况下更改了密钥“类型”
正在发送。检查QBElement类的KVO合规性
我有什么明显的遗漏吗?为什么会出现此错误?实现手动设置器并不意味着您必须实现手动KVO通知。只有在更新属性的支持ivar而不通过setter时,才绝对需要这些。KVO将自动用调用“真实”setter前后调用will/didChange方法的版本替换setter。如果您想自己在setter中调用它们(例如,您需要更精确地控制调用它们的时间),则需要重写方法以返回
类型
键的否
我假设您现在出现错误的原因是因为KVO机器看到您连续调用了两次
[self willChangeValueForKey:@“type”]
(一次通过KVO“magic”,一次手动),而没有调用[self didChangeValueForKey:@“type”]
您不需要显式调用-willChangeValueForKey:
和-didChangeValueForKey:
,因为您的setter命名正确。对它们的调用将通过KVC/KVO机器的魔力自动添加。问题可能是他们实际上被呼叫了两次
此外,由于minA
和maxA
似乎只是从该类型派生而来,因此您可以将它们设置为只读,并告诉KVO在类型更改时自动通知观察者minA和maxA已更改:
@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
正如其他人所指出的,您可以放弃willChangeValueForKey:
,didChangeValueForKey:
调用并实现:
+ (NSSet *) keyPathsForValuesAffectingMinA
{
return [NSSet setWithObject:@"type"];
}
当然,minB
也一样
然后设置minA
和minB
只读属性
不同的方法主要是风格偏好。我尝试过在没有willChangeValueForKey:
和didChange的情况下运行应用程序。
但用户界面从未收到更改通知!(旁注:minA本身就是一个变量,它可能不会以这种方式出现,因为我简化了这个问题的代码)在这种情况下,还有其他错误。您真的不需要在这样的setter中调用willChange…
和didChange…
。至于minA
本身就是一个变量,我理解,在这种情况下,我的建议行不通。但是,也许您应该发布完整的代码,以防简化更改或消除问题。