iOS学习——KVO底层实现机制探究

一、什么是KVO?

KVO(Key Value Observing, 键值观察)是Objective-C对观察者模式的实现,每次当被观察对象的某个属性值发生改变时,注册的观察者便能获得通知。

使用KVO很简单,分为三个基本步骤:

①、注册观察者,指定被观察对象的属性:

[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
其中,person即为被观察对象,它的name属性即为被观察的属性。

②、在观察者中实现以下回调方法:

- (void)observeValueForKeyPath:(NSString *)keyPath  
                      ofObject:(id)object  
                        change:(NSDictionary *)change  
                       context:(voidvoid *)context  
   
{  
    // use the context to make sure this is a change in the address,  
    // because we may also be observing other things 

        NSString *name = [object valueForKey:@"name"]; 
        NSLog(@"new name is: %@", name);  
}  

只要person对象中的name属性发生变化,系统会自动调用该方法。

③、最后,不要忘记在dealloc中移除观察者

-(void)dealloc  
{  
    // must stop observing everything before this object is  
    // deallocated, otherwise it will cause crashes  
    for(Person *p in m_observedPeople){  
        [p removeObserver:self forKeyPath:@"name"];  
    }  
   
    [m_observedPeople release];  
    m_observedPeople = nil;  
}  

二、KVO的底层是如何实现的?

1、要探究KVO的底层实现机制,就不得不了解OC的强大的runtime特性。runtime是一套纯C语言实现的API,我们编写的OC程序在运行时最终都是转换成了runtime实现的C代码,比如,OC中的方法调用:[receiver doSomething]; 在运行时都会转换成消息发送代码:objc_msgSend(receiver, @selector(doSomething));,其中,receiver是消息的接收者,@selector(doSomething)是方法的实现。

runtime技术非常的强大,能够在程序运行时获取并修改类的各种信息:

  • 获取类的属性列表(class_copyPropertyList)和每个属性的名称(property_getName);
  • 拦截调用,将无法处理的selector转发给其他的对象(forwardingTargetForSelector);
  • 在某个类无法处理selector时,动态添加方法(resolveInstanceMethod);
  • 当想向某个类添加属性时,使用objc_setAssociatedObject;
  • 获取类的所有方法列表(class_copyMethodList);
  • 在运行时动态地对类替换或添加新的函数(class_replaceMethod、class_addMethod),又叫:Swizzle Method技术。
想获取runtime的详细介绍,请戳这里。
2、想快速地了解OC中使用的某项技术,最快捷高效的莫过于查看Apple的官方文档。但是关于KVO的具体实现原理,Apple的文档介绍的真是Can't Be Simple Any More!


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.

 从介绍可以看出,KVO的实现用了所谓的“isa-swizzling”的技术,但是具体是怎么实现的却不得而知。不过,如果用runtime提供的方法去深入探究,便可以窥探其详细的原理。得益于Mike Ash的文章,我们可以详细了解KVO实现的技术细节。

简单介绍一下KVO的实现原理:

当设置一个类为观察对象时,系统会动态地创建一个新的类,这个新的类继承自被观察对象的类,还重写了基类被观察属性的setter方法。派生类在被重写的setter方法中实现真正的通知机制。最后,系统将这个对象的isa指针指向这个新创建的派生类,这样,被观察对象就变成了新创建的派生类的实例。(注:runtime中,对象的isa指针指向该对象所属的类,类的isa指针指向该类的metaclass。有关OC的对象、类对象、元类对象metaclass object和isa指针,请戳这里详细了解)。同时,新的派生类还重写了dealloc方法(removeObserver)。

3、KVO的缺陷

不可否认,KVO的功能确实很强大,但是它的缺点也很明显:

①、过于简单的API

KVO中只有通过重写-observeValueForKeyPath:ofObject:change:context方法来获取通知,该方法有诸多限制:不能使用自定义的selector,不能使用block,而且当父类也要监听对象时,往往要写一大坨代码。

②、父类和子类同时存在KVO时(监听同一个对象的同一个属性),很容易出现对同一个keyPath进行两次removeObserver操作,从而导致程序crash(感谢:http://www.cnblogs.com/wengzilin/p/4346775.html)。要避免这个问题,就需要区分出KVO是self注册的,还是superClass注册的,我们可以在 -addObserver:forKeyPath:options:context:和-removeObserver:forKeyPath:context这两个方法中传入不同的context进行区分。

三、自己实现KVO

待续。。。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值