代码改变世界

KVC, KVO实现原理剖析

2015-12-31 16:44  JG2014  阅读(243)  评论(0编辑  收藏  举报

iPhone程序开发 KVO/KVC实现机理分析是本文要介绍的内容,不多说,直接进入话题。我们来看详细内容。

Objective-C里面的Key-Value Observing (KVO)机制,非常不错,可以很好的减少浇水代码。关于KVO的学习,可以参考文章:《Key-Value Observing快速入门》:http://www.cocoadev.cn/Objective-C/Key-Value-Observing-Quick-Start-cn.asp

Key-Value Coding(KVC)实现分析

KVC运用了一个isa-swizzling技术。isa-swizzling就是类型混合指针机制。KVC主要通过isa-swizzling,来实现其内部查找定位的。isa指针,如其名称所指,(就是is a kind of的意思),指向维护分发表的对象的类。该分发表实际上包含了指向实现类中的方法的指针,和其它数据。

比如说如下的一行KVC的代码:

[site setValue:@"sitename" forKey:@"name"];

就会被编译器处理成:

SEL sel = sel_get_uid ("setValue:forKey:");

IMP method = objc_msg_lookup (site->isa,sel);

method(site, sel, @"sitename", @"name");

Selectors

Selectors are the run-time system's identifier for a method. TheSELdata type is used for selectors.

Thesel_get_uid()function can be used to get a method's selector from it's name:

objc_msg_lookup

If we want to get anIMPusing the Objective-C runtime functions, then useobjc_msg_lookup(id,SEL)on the GNU runtime.

What is an IMP? How do I get one?

IMPis a C type referring to theimplementationof a method, also known as animplementation pointer. It's a pointer to a function returningid, and withselfand amethod selector(available inside method definitions as the variable_cmd) as the first arguments:

id (*IMP)(id, SEL, ...);

With NSObject, you can obtain theIMPfor a given method by doing:

IMP imp=[obj methodForSelector:@selector(message)];

For Object, do:

IMP imp=[obj methodFor:@selector(message)];

How do I send a message given an IMP?

Dereference it, as with a C function pointer:

id anObject, theResult;

IMP someImp;

SEL aSelector;

// ...

theResult=someImp(anObject,aSelector);

=================

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.

Instead of relying on the isa pointer your application should use the class method to determine the class of an object instance.

首先介绍两个基本概念:

(1)SEL数据类型:它是编译器运行Objective-C里的方法的环境参数。

(2)IMP数据类型:他其实就是一个 编译器内部实现时候的函数指针。当Objective-C编译器去处理实现一个方法的时候,就会指向一个IMP对象,这个对象是C语言表述的类型(事实上,在Objective-C的编译器处理的时候,基本上都是C语言的)。

关于如何找到实现函数的指针,可参考文章:《Objective-C如何避免动态绑定,而获得方法地址》:http://www.cocoadev.cn/Objective-C/Get-method-address.asp

这下KVC内部的实现就很清楚的清楚了:一个对象在调用setValue的时候,(1)首先根据方法名找到运行方法的时候所需要的环境参数。(2)他会从自己isa指针结合环境参数,找到具体的方法实现的接口。(3)再直接查找得来的具体的方法实现。

Key-Value Observing(KVO)实现

在上面所介绍的KVC机制上加上KVO的自动观察消息通知机制就水到渠成了。

当观察者为一个对象的属性进行了注册,被观察对象的isa指针被修改的时候,isa指针就会指向一个中间类,而不是真实的类。所以isa指针其实不需要指向实例对象真实的类。所以我们的程序最好不要依赖于isa指针。在调用类的方法的时候,最好要明确对象实例的类名。

熟悉KVO的朋友都知道,只有当我们调用KVC去访问key值的时候KVO才会起作用。所以肯定确定的是,KVO是基于KVC实现的。其实看了上面我们的分析以后,关系KVO的架构的构思也就水到渠成了。

因为KVC的实现机制,可以很容易看到某个KVC操作的Key,而后也很容易的跟观察者注册表中的Key进行匹对。假如访问的Key是被观察的Key,那么我们在内部就可以很容易的到观察者注册表中去找到观察者对象,而后给他发送消息。

总结一下,想使用KVO有三种方法:

1)使用了KVC

使用了KVC,如果有访问器方法,则运行时会在访问器方法中调用will/didChangeValueForKey:方法;

没用访问器方法,运行时会在setValue:forKey方法中调用will/didChangeValueForKey:方法。

2)有访问器方法

运行时会重写访问器方法调用will/didChangeValueForKey:方法。

因此,直接调用访问器方法改变属性值时,KVO也能监听到。

3)显示调用will/didChangeValueForKey:方法。