博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS KVO
阅读量:6338 次
发布时间:2019-06-22

本文共 2934 字,大约阅读时间需要 9 分钟。

KVO的能力

KVO全称 key value observing,用于监听对象属性的改变,可以监听多个属性。

使用方法

只需要复写-addObserver:forKeyPath:options:context方法即可,如果监听多个属性,需要在方法中通过keyPath来判断修改的是哪一个属性。在更复杂的业务场景下,使用 context 上下文以及其它辅助手段才能够帮助我们更加精准地确定被观测的对象。尤其是处理那些继承自同一个父类的子类,并且这些子类有相同的 keypath。

// Foo.h@interface Foo : NSObject@property (nonatomic, copy) NSString *bar;@end复制代码
// Foo.m#import "Foo.h"#import 
@implementation Foo- (void)setBar:(NSString *)bar{ NSLog(@"self->isa = %@", object_getClass(self)); _bar = bar;}复制代码
ViewController.m#import "ViewController.h"#import "Foo.h"#import 
@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. Foo *foo = [[Foo alloc] init]; NSLog(@"self->isa = %@", object_getClass(foo)); [foo addObserver:self forKeyPath:@"bar" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; NSLog(@"self->isa = %@", object_getClass(foo)); NSLog(@"self->isa->superClass = %@", class_getSuperclass(object_getClass(foo))); foo.bar = @"祈求者Kael";}- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary
*)change context:(void *)context{ if ([keyPath isEqualToString:@"bar"]) { NSLog(@"change = %@", change[NSKeyValueChangeOldKey]); NSLog(@"change = %@", change[NSKeyValueChangeNewKey]); }}@end复制代码
2019-05-24 15:49:19.916170+0800 NSCodingDemo[1614:88021] self->isa = Foo2019-05-24 15:49:19.916548+0800 NSCodingDemo[1614:88021] self->isa = NSKVONotifying_Foo2019-05-24 15:49:19.916548+0800 NSCodingDemo[1614:88021] self->isa->superClass = Foo2019-05-24 15:49:19.916667+0800 NSCodingDemo[1614:88021] self->isa = NSKVONotifying_Foo2019-05-24 15:49:19.916799+0800 NSCodingDemo[1614:88021] change = 
2019-05-24 15:49:19.916898+0800 NSCodingDemo[1614:88021] change = 祈求者Kael复制代码

实现原理

众所周知,KVO是通过iOS runtime的isa-swizzle来实现的。

从上面代码的打印结果可以看出,一旦使用 addObserver:forKeyPath:options:context:给实例foo添加观察者之后,系统会创建一个新类NSKVONotifying_Foo,并且NSKVONotifying_Foo是继承自Foo。然后把实例foo的isa指针从 Foo 变成了 NSKVONotifying_Foo,这样这个原本的Foo类实际就变成了NSKVONotifying_Foo( 因为isa 指针告诉 Runtime 系统这个对象的类是什么),最后,苹果爸爸还重写NSKVONotifying_Foo的class方法,使它返回父类Foo,造成这个类还是Foo的错觉,从而使上述一系列的骚操作不被察觉。

系统重写了NSKVONotifying_Foo类的setter方法,在调用父类的setter方法前后,插入了willChangeValueForKey:didChangeValueForKey:方法。

正式由于上述,KVO 行为是同步的,并且发生与所观察的值发生变化的同样的线程上。

手动触发KVO

手动调用willChangeValueForKey:didChangeValueForKey:方法,即可在不改变属性值的情况下手动触发KVO,willChangeValueForKey:用于记录旧值,didChangeValueForKey用于记录新值。

并且这两个方法缺一不可。
例如,对于属性bar,只有当新值与旧值不同时,才触发观察的代码逻辑。

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{    if ([key isEqualToString:@"bar"]) {        return NO;    }    return YES;}- (void)setBar:(NSString *)bar{    if ([_bar isEqualToString:bar]) {        return;    }    [self willChangeValueForKey:@"bar"];    _bar = bar;    [self didChangeValueForKey:@"bar"];}复制代码

参考

转载地址:http://soooa.baihongyu.com/

你可能感兴趣的文章
多线程基础(三)NSThread基础
查看>>
PHP的学习--Traits新特性
查看>>
ubuntu下,py2,py3共存,/usr/bin/python: No module named virtualenvwrapper错误解决方法
查看>>
Ext.form.field.Number numberfield
查看>>
Linux文件夹分析
查看>>
解决部分月份绩效无法显示的问题:timestamp\union al\autocommit等的用法
查看>>
nginx 域名跳转 Nginx跳转自动到带www域名规则配置、nginx多域名向主域名跳转
查看>>
man openstack >>1.txt
查看>>
linux几大服务器版本大比拼
查看>>
在BT5系统中安装postgresQL
查看>>
【Magedu】Week01
查看>>
写给MongoDB开发者的50条建议Tip25
查看>>
为什么要让带宽制约云计算发展
查看>>
2012-8-5
查看>>
VS中ProjectDir的值以及$(ProjectDir)../的含义
查看>>
我的友情链接
查看>>
PHP实现排序算法
查看>>
Business Contact Mnanager for Outlook2010
查看>>
9种用户体验设计的状态是必须知道的(五)
查看>>
解决WIN7下组播问题
查看>>