Objective-C报”NSKeyValueChangeKindKey”异常的原因和解决办法

  • Post category:IOS

Objective-C 中的 "NSKeyValueChangeKindKey" 异常通常是因为使用了错误的 key path 或者监听属性的对象没有遵循 KVO(键值观察)协议。下面我将详细讲解异常的原因和解决方法,并提供两个示例:

原因

一般情况下,KVO 的使用是需要三个步骤:

  1. 注册观察者。
  2. 添加观察者。
  3. 实现监听方法。

如果其中任何一个步骤有误,调用观察者时就会报 "NSKeyValueChangeKindKey" 异常。

解决办法

如果出现了该异常,我们需要进行下面的操作:

  1. 首先检查 key path 是否被正确的监听。
  2. 确保你的监听对象实现了 NSObjectclassNSObjectNSObject 遵循 KVO 协议的方法。
  3. 使用正确的 key path。
  4. 检查使用的 key path 的值, 若要监听某一属性,该属性的值必须为对象。
  5. 确保观察者的添加和删除操作成对出现。

下面是两个示例:

示例 1:错误的 key path

// 关键路径中应该为 "name",而不是 "names"
[person addObserver:self forKeyPath:@"names" options:NSKeyValueObservingOptionNew context:nil];

上面的代码中,Person 对象中不存在 “names” 属性,因此调用观察者时就会报 "NSKeyValueChangeKindKey" 异常。解决方法是将关键路径修改为正确的值 “name” :

[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

示例 2:监听对象没有遵循 KVO 协议

@interface Person : NSObject
@property (nonatomic, assign) NSUInteger age;
@end

@implementation Person
@end

// 注册观察者并添加观察者
[person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];

// 相关方法实现
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"age"]) {
        // 获取 age 属性的新值
        NSUInteger newAge = [change[NSKeyValueChangeNewKey] intValue];
        NSLog(@"Person's age has changed to: %ld", newAge);
    }
}

上面的代码中Person 类中并没有遵循 KVO 协议,即没有实现 NSObjectclassNSObjectNSObject 遵循 KVO 协议的方法。因此调用观察者时就会报 "NSKeyValueChangeKindKey" 异常。解决方法是将 Person 类遵循 KVO 协议:

@interface Person : NSObject
@property (nonatomic, assign) NSUInteger age;
@end

@implementation Person

// 遵循 KVO 协议
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    return YES;
}

@end

// 注册观察者并添加观察者
[person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];

// 相关方法实现
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"age"]) {
        // 获取 age 属性的新值
        NSUInteger newAge = [change[NSKeyValueChangeNewKey] intValue];
        NSLog(@"Person's age has changed to: %ld", newAge);
    }
}

上述代码中,我们使用了 +[NSObject automaticallyNotifiesObserversForKey:] 方法告诉 KVO,当 age 属性发生变化时,对象已经自动通知过观察者,观察者不用再次调用 willChangeValueForKey:didChangeValueForKey: 方法通知对象的改变了,而是直接获取 age 的新值。配合此方法,就可以正确监听到 age 属性的变化了。

以上就是 Objective-C 报 “NSKeyValueChangeKindKey” 异常的原因和解决方式,希望能对你有所帮助。