KVC/KVO实现的根本是Objective-C的动态性和runtime动态运行时isa-swizzling方法
1、KVC简介
全称是Key-value coding,翻译成键值编码。顾名思义,在某种程度上跟map的关系匪浅。它提供了一种使用字符串而不是访问器方法去访问一个对象实例变量的机制。
2、KVO简介
全称是Key-value observing,翻译成键值观察。提供了一种当其它对象属性被修改的时候能通知当前对象的机制。再MVC大行其道的Cocoa中,KVO机制很适合实现model和controller类之间的通讯。
实现原理
1、KVC如何访问属性值?
在某种程度上KVC提供了访问器的替代方案。
不过访问器方法是一个很好的东西,以至于只要是有可能,KVC也尽量再访问器方法的帮助下工作。
为了设置或者返回对象属性,KVC按顺序使用如下技术:
首先查找Set<Key>方法,然后按照_key,_iskey,key,iskey的顺序搜索方法。
如果仍为找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。这两个方法的默认实现都是抛出异常,我们可以根据需要重写它们。
2、KVC相关技术
2.1、 Key和Key Path
KVC定义了一种按名称访问对象属性的机制,支持这种访问的4个主要方法是:
-(id)valueForKey:(NSString *)key;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
前边两个方法用到的Key较容易理解,就是要访问的属性名称对应的字符串。
后面两个方法用到的KeyPath是一个被点操作符隔开的用于访问对象的指定属性的字符串序列。比如KeyPath address.street将会访问消息接收对象所包含的address属性中包含的一个street属性。其实KeyPath说白了就是我们平时使用点操作访问某个对象的属性时所写的那个字符串。
2.2、点语法和KVC
在实现了访问器方法的类中,使用点语法和KVC访问对象其实差别不大,二者可以任意混用。但是没有访问起方法的类中,点语法无法使用,这时KVC就有优势了。( 原因见第三部分的第一节:KVC如何访问属性值。
2.3、键值验证(Key-Value Validation)
KVC提供了验证Key对应的Value是否可用的方法:
- (BOOL)validateValue:(inout id *)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
该方法默认的实现是调用一个如下格式的方法:
- (BOOL)validate<Key>:error:
注意:KVC是不会自动调用键值验证方法的 ,就是说我们需要手动验证。但是有些技术,比如CoreData会自动调用。
KVO本质
当某个类的实例对象的key第一次被观察时,系统就会在运行期动态地创建该类的一个派生类NSKVONotifying_类名,在这个派生类中重写该类中被观察的属性的 setter 方法。
KVO的原理是什么?
苹果官方文档
Key-Value Observing Implementation Details
Automatic key-value observing is implemented using a technique called isa-swizzling.
The isa pointer, as the name suggests, points to the object's class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data.
When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.
You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.
实现原理:
当某个类的对象被观察时,系统的runtime运行时会动态的实现一个基于该类的一个中间类(派生类),在中间类中实现被观察基类属性的setter方法,中间类在重写的setter方法中实现真正的通知机制。
**同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。
**
新的中间类NSKVONotifying_Person会重写监听的属性对应的以下方法:
set方法、class方法、dealloc方法、_isKVO
①class
重写class方法是为了我们调用它的时候返回跟重写继承类之前同样的内容。
②重写set方法
新类会重写对应的set方法,是为了在set方法中增加另外两个方法的调用:
- (void)willChangeValueForKey:(NSString *)key
- (void)didChangeValueForKey:(NSString *)key
其中,didChangeValueForKey:方法负责调用:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
这就是KVO实现的原理!
如果没有任何的访问器方法,-setValue:forKey方法会直接调用:
- (void)willChangeValueForKey:(NSString *)key
- (void)didChangeValueForKey:(NSString *)key
如果在没有使用键值编码且没有使用适当命名的访问起方法的时候,我们可以直接调用上述两个方法,同样可以使用KVO!
参考:
原文作者wangzz
[深入浅出Cocoa]详解键值观察(KVO)及其实现机理](http://blog.csdn.net/kesalin/article/details/8194240)
如果你对类和对象的关系不太明白阅读:深入浅出Cocoa之类与对象