KVO&KVC分析

KVO和KVC应用还是比较广泛的。所以,今天我们就重新再学习下它们,以加深记忆。

一、KVO

1、KVO(key-value observing)定义

顾名思义“键值观察”,当观察对象发生改变的时候,对象会得到通知,然后做出相应的处理。观察者观察的是属性,只有遵循 KVO 变更属性值的方式才会执行 KVO 的回调方法,例如是否执行了 setter 方法、或者是否使用了 KVC 赋值。
如果赋值没有通过 setter 方法或者 KVC,而是直接修改属性对应的成员变量,例如:仅调用 _name = @"newName",这时是不会触发 KVO 机制,更加不会调用回调方法的。
所以使用 KVO 机制的前提是遵循 KVO 的属性设置方式来变更属性值。

2、KVO的基本原理
  • KVO是基于runtime机制实现的

  • 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制

  • 如果原类为Person,那么生成的派生类名为NSKVONotifying_Person

  • 每个类对象中都有一个isa指针指向当前类,当一个类对象第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法

  • 键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。

3、使用流程

a、注册观察者,实施监听;
b、在回调方法中处理属性发生的变化;
c、移除观察者;

4、方法分析

a、注册观察者

//第一个参数 observer:观察者 (这里观察self.myKVO对象的属性变化)
//第二个参数 keyPath: 被观察的属性名称(这里观察 self.myKVO 中 num 属性值的改变)
//第三个参数 options: 观察属性的新值、旧值等的一些配置(枚举值,可以根据需要设置,例如这里可以使用两项)
//第四个参数 context: 上下文,可以为 KVO 的回调方法传值(例如设定为一个放置数据的字典)
[self.myKVO addObserver:self forKeyPath:@"num" options:
NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

b、属性变化收到的通知

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

二、KVC

KVC(Key-value coding)顾名思义键值编码。简单来说,是可以通过对象属性名称(Key)直接给属性值(value)编码(coding),也可以说赋值。这样可以免去调用setter、getter方法,从而简化我们的代码。最最重要的是,它还可以用来修改系统控件内部的属性,我们在自定义控件属性的时候经常会用到(需要配合runtime)。

#import <Foundation/Foundation.h>
#import "KVO.h"
@interface KVC : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,retain) KVO *product;
@end
#import <Foundation/Foundation.h>

@interface KVO : NSObject
@property (nonatomic,assign) int num;
@property (nonatomic,copy) NSString *productName;
@end

使用场景:
 1、runtime中修改属性参数的地方,如导航栏透明、pageControl的背景图片。
 2、普通的对属性赋值。不过我们一般就直接访问属性了,这么做反而会麻烦些,且效率不高。

注意:KVC的性能并不能直接访问属性快,虽然这个性能消耗是微乎其微的。所以在使用KVC的时候,建议最好不要手动设置属性的setter、getter,这样会导致搜索步骤变长。
而且尽量不要用KVC进行集合操作,例如NSArray、NSSet之类的,集合操作的性能消耗更大,而且还会创建不必要的对象。

 
    KVC *model = [[KVC alloc]init];

    self.myKVO = [[KVO alloc]init];//大哥,别忘了初始化啊,这么低级的错误。
    /*
     1、未使用KVC的赋值
     myKVC.name = @"zhangsan";
     self.myKVO.productName = @"productName";
     myKVC.KVOModel = self.myKVO;
     self.myKVO.productName = @"productName";
     
     NSLog(@"---------------kvo:%@",self.myKVO.productName);
     */
    //2、使用KVC的赋值
    [model setValue:@"wangwu" forKey:@"name"];
 
    model.product = self.myKVO;
    NSLog(@"----------KVC:%@",[model valueForKeyPath:@"name"]);

    /*
     直接提取KVOModel中的属性值,用“.”分割
     因为类key反复嵌套,所以有个keyPath的概念,keyPath就是用.号来把一个一个key链接起来,这样就可以根据这个路径访问下去.
     */
    [model setValue:@"zhangsanfeng" forKeyPath:@"product.productName"];

    NSLog(@"----------kvc:%@",[model valueForKeyPath: @"product.productName"]);

runtime配合KVC修改属性

功能:修改UINavigationBar的左右两边的视图透明度。
注意:我们获取的_liftViews,_rightViews发现在iOS11里面不能用了,使用的话会崩溃。我们这里列出来它的调用方法作为示例分析。
[[self valueForKey:@"_liftViews"] enumerateObjectsUsingBlock:^(UIView *view, NSUInteger i, BOOL *stop) {
        view.alpha = alpha;
    }];

    [[self valueForKey:@"_rightViews"] enumerateObjectsUsingBlock:^(UIView *view, NSUInteger i, BOOL *stop) {
        view.alpha = alpha;
    }];

    UIView *titleView = [self valueForKey:@"_titleView"];
    titleView.alpha = alpha;

三、扩展

1、KVC与KVO的不同?

KVC(键值编码),即使用字符串访问一个实例变量的机制。而不是通过调用setter/getter等方法显式的存储方式访问。

2、notification比KVO有什么区别?

a、notification比KVO多了发送通知的一步,但是对象之间的直接交互,通知要明显的多,需要notificationCenter来做为中间交互。而KVO如我们介绍的,设置观察者->处理属性变化,至于中间通知这一环,是由系统处理的。
b、notification的优点时监听不局限于属性的变化,还可以对多种多样的变化进行监听,范围更广泛。(键盘、前后台等)

3、和delegate的不同?

相同点:
a、delegate,KVO和NSNotification的作用都是类与类之间的通信。
不同点:
a、KVO和NSNotification都是负责发送接收通知,剩下的事情交给系统处理,所以不用返回值,而delegate 则需要通信的对象通过变量(代理)联系
b、delegate是一对一,KVO和NSNotification这两个可以一对多。

Demo地址:https://github.com/caiqingchong/KVO-KVC.git
参考链接:
https://www.jianshu.com/p/e59bb8f59302
https://www.jianshu.com/p/7ba3d0eb4908

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

推荐阅读更多精彩内容