聊聊 KVC KVO

KVC

NSKeyValueCoding 协议 键值编码(key-value),指定的key获取对应value;
一个类中的私有变量,不能直接通过Setter、Getter方法访问。但是却可以通过KVC来访问。利用kvc的这个特性,我们可以访问系统里的一些私有变量。

valueForKey:
setValue:@"" forKey:@"keypath";
可以直接设置key,keyPath 为属性和成员变量名称;Key:只能访问当前对象的属性
KeyPath:能利用运算符一层一层往内部访问属性;

<1> .语法 和 kvc
在实现了访问器方法的类中,使用点语法和KVC访问对象其实差别不大,二者可以任意混用;

<2> kvc 中的数值和数据结构

NSNumber 是一般基础数据类型的对象;
NSValue 主要是对一些结构体型的数据和自定义的数据结构类型;

那么我们对应属性名称作为 keyPath,系统是如何帮助我们查找到对应成员变量和属性的呢?

① 检查是否存在-、-is(只针对布尔值有效)或者-get的访问器方法,如果有可能,就是用这些方法返回值;
检查是否存在名为-set:的方法,并使用它做设置值。对于-get和-set:方法,将大写Key字符串的第一个字母,并与Cocoa的方法命名保持一致;

②如果上述方法不可用,则检查名为-_、-_is(只针对布尔值有效)、-_get和-set:方法;
③如果没有找到访问器方法,可以尝试直接访问实例变量。实例变量可以是名为:
;

④如果仍未找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。这些方法的默认实现都是抛出异常,我们可以根据需要重写。

异常处理 valueForUndefinedKey:

/* Given that an invocation of -valueForKey: would be unable to get a keyed value using its default access mechanism, return the keyed value using some other mechanism. The default implementation of this method raises an NSUndefinedKeyException. You can override it to handle properties that are dynamically defined at run-time.
*/

对于 NSDictionary objectForkey: 与 valueForKey:区别

一般来说 key 可以是任意字符串组合,如果 key 不是以 @ 符号开头,这时候 valueForKey: 等同于 objectForKey:,如果是以 @ 开头,去掉 key 里的 @ 然后用剩下部分作为 key 执行 [super valueForKey:]。

NSDictionary *dict = [NSDictionary dictionaryWithObject:@"theValue" forKey:@"@theKey"];
NSString *value1 = [dict objectForKey:@"@theKey"];
NSString *value2 = [dict valueForKey:@"@theKey"];

value1  ok, value2 crash 
NSUnknownKeyException’, reason: ‘[<__NSCFDictionary 0x892fd80> valueForUndefinedKey:]: 
在 KVC 里可以通过 property 同名字符串来获取对应的值。 @"@theKey"会转成@"theKey",没有同名执行valueForUndefinedKey 抛出异常;

KVO

NSObject(NSKeyValueObserving)

  • 注册监听
  • 监听事件处理
  • 取消监听

当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。
派生类在被重写的 setter 方法实现真正的通知机制,就如前面手动实现键值观察那样。这么做是基于设置属性会调用setter方法,通过下面两个方法实现:

  • (void)willChangeValueForKey:(NSString *)key
  • (void)didChangeValueForKey:(NSString *)key;
didChangeValueForKey 会处理:
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context;

 
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7, 5_0);

KVO 和 NSNotificationCenter 苹果实现是只保存了个对象的地址,并没有在销毁的时候置为nil。所以 addObserver后,必须要有remove操作。

你可能感兴趣的:(聊聊 KVC KVO)