KVO学习

KVO机制学习

什么是KVO?

  KVO是Key-Value-Observing的缩写,通过KVO机制对象可以得到其他对象的某个属性的变更通知。这种机制在MVC模式下显得更为重要,KVO可以让视图对象经过控制器观察模型对象的变更从而做出更新等操作。KVO不仅是Objective-C对观察者模式(Observer Pattern)的实现,也是Cocoa Binding的基础。

KVO怎么用?

  KVO这一机制是基于NSKeyValueObserving协议的,Cocoa通过这个协议为所有遵循协议的对象提供了自动观察属性变化的能力。在NSObject中已经为我们实现了这一协议,所以我们不必去实现这个协议。

使用步骤:
1.注册观察者,实施监听;

//observer:观察者
//keyPath: 被观察的属性名称
//options: 观察属性的新值、旧值等的一些配置(枚举值,可以根据需要设置,例如这里可以使用两项)
//第四个参数context: 上下文,可以为kvo的回调方法传值(例如设定为一个放置数据的字典)
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

2.观察者实现回调方法,在回调方法中处理属性发生的变化;

//keyPath:属性名称
//object:被观察的对象
//change:变化前后的值都存储在change字典中
//context:注册观察者时,context传过来的值
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context;

3.移除观察者:

//observer:观察者
//keyPath:属性名称
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

实例:

//被观察者
@interface TestClass : NSObject

@property (nonatomic, assign)int x;
@property (nonatomic, assign)int y;

@end

@implementation TestClass

@end

//观察者
@interface ObserverClass: NSObject

@property (nonatomic, strong) TestClass *obj;

- (instancetype)initWith:(TestClass *)obj;

@end

@implementation ObserverClass

- (instancetype)initWith:(TestClass *)obj
{
    if(self = [super init])
    {
        //不能写_obj = obj; 外部的obj发生变化,内部的_obj也会同步变化,因为_obj与obj都指向同一地址
        _obj = [[TestClass alloc]init];
        _obj.x = obj.x;
        _obj.y = obj.y;
    }
    return self;
}

//观察者类需要实现的回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if([keyPath isEqualToString:@"x"] && object)
    {
        _obj.x = ((TestClass *)object).x;
    }
    else if([keyPath isEqualToString:@"y"] && object)
    {
        _obj.y = ((TestClass *)object).y;
    }
}

@end

//在main方法中定义观察者对象和被观察者对象
int main(int argc, char * argv[]) {
    @autoreleasepool {
        TestClass *obj = [[TestClass alloc]init];
        obj.x = 1;
        obj.y = 2;
        
        ObserverClass *observer = [[ObserverClass alloc]initWith:obj];
        
        NSLog(@"before adding Observer");
        NSLog(@"Observer x:%d", observer.obj.x);
        NSLog(@"Observer y:%d", observer.obj.y);
        
        //添加观察者
        [obj addObserver:observer forKeyPath:@"x" options:NSKeyValueObservingOptionNew context:nil];
        [obj addObserver:observer forKeyPath:@"y" options:NSKeyValueObservingOptionNew context:nil];
        
        obj.x = 3;
        obj.y = 4;
        NSLog(@"after adding Observer");
        NSLog(@"Observer x:%d", observer.obj.x);
        NSLog(@"Observer y:%d", observer.obj.y);
        
        //移除观察者
        [obj removeObserver:observer forKeyPath:@"x"];
        [obj removeObserver:observer forKeyPath:@"y"];
        return 0;
    }
}

为什么使用KVO?

  1. 我们创建一两个setter方法感觉没什么,但是如果要观察的属性非常多,那么还能一一重写setter方法来实现吗?想必大家心里已有了答案,但是利用KVO则能很好的解决上述问题。

  2. 我们自定义的类是很容易改写setter方法的,但是如果你是用一个已经编译好了的类库时要监控其中一个属性时怎么办?难道还要去重写setter方法?如果使用KVO则很轻松解决问题。

  3. 使用KVO能够方便的记录变化前的值和变化后的值,不使用KVO你还要自己来解决这些问题。

  4. KVO让你的代码看起来更加简洁清晰易于维护。

KVO的特点

  观察者观察的是属性,只有遵循 KVO 变更属性值的方式才会执行KVO的回调方法,例如是否执行了setter方法、或者是否使用了KVC赋值。
  如果赋值没有通过setter方法或者KVC,而是直接修改属性对应的成员变量,例如:仅调用_name = @"newName",这时是不会触发kvo机制,更加不会调用回调方法的。
所以使用KVO机制的前提是遵循 KVO 的属性设置方式来变更属性值。

KVO的实现原理

  KVO 的实现也依赖于 Objective-C 强大的 Runtime,Apple 使用了 isa 混写(isa-swizzling)来实现 KVO。swizzling是不是很熟悉,在Method Swizzling中我们修改了Method的IMP指向,isa-Swizzling就是修改了isa指针的指向。
  首次观察某个object时,runtime会创建一个新的继承原先class的subclass。在这个新的class中,它重写了所有被观察的key,然后将object的isa指针指向新创建的class(这个指针告诉Objective-C运行时某个object到底是哪种类型的object)。所以object神奇地变成了新的子类的实例。
  这些被重写的方法实现了如何通知观察者们。当改变一个key时,会触发setKey方法,但这个方法被重写了,并且在内部添加了发送通知机制。(当然也可以不走setXXX方法,比如直接修改iVar,但不推荐这么做)。
  KVO的键值观察通知依赖于 NSObject 的两个方法:

  1. willChangeValueForKey:在被观察属性发生改变之前调用,通知系统该 keyPath 的属性值即将变更
  2. didChangevlueForKey:属性改变发生后调用,通知系统该 keyPath 的属性值已经变更;

  之后观察者实现的observeValueForKey:ofObject:change:context:也会被调用。并且重写观察属性的setter方法这种继承方式的注入是在运行时而不是编译时实现的。
  有意思的是:苹果不希望这个机制暴露在外部。除了setters,这个动态生成的子类同时也重写了-class方法,依旧返回原先的class!如果不仔细看的话,被KVO过的object看起来和原先的object没什么两样。

以下是在网上找到的一张图,比较形象地描述了KVO的实现原理:


KVO实现原理

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 在编程中,最常见的就是程序的流程取决于你所使用的各种变量和属性的值,根据变量和属性的值确定后面运行的代码,有时会检...
    pro648阅读 1,633评论 2 27
  • 上半年有段时间做了一个项目,项目中聊天界面用到了音频播放,涉及到进度条,当时做android时候处理的不太好,由于...
    DaZenD阅读 3,011评论 0 26
  • [深入浅出Cocoa]详解键值观察(KVO)及其实现机理罗朝辉 (http://www.cppblog.com/k...
    Crazy2015阅读 677评论 0 1
  • kvo 一、kvo使用 1.添加观察者 给某个需要监听的对象添加一个观察者。 各个参数的意义是: observer...
    cr7aa阅读 279评论 0 0