KVC&KVO

KVC(key-value coding)

概要

KVC允许开发者通过名字访问属性,无需调用明确的存取方法,这样开发者就能在运行时确定属性的绑定,而不是在编译时。

  • 访问器或实例变量在程序内书写时只能访问该程序的属性。例如setName:,那么在这个地方就只能访问一个属性。与此相对,由于键可以保存在字符串变量中,因此根据变量值是“name”还是“age”,就可以访问不同的属性。

基本处理

KVC允许开发者通过名字()访问属性
常用的方法:

- (id)valueForKey:(NSString *)key;
- (void)setValue:(id)value forKey:(NSString *)key;

还有一种比较常用但很好用的方法,如下所示

//KVC常用方法的使用示例如下
id aPerson = [aGroup valueForKey:@"leader"];
id name = [aPerson valueForKey:@"name"];
//上面的实例可以简化为下面这行代码
id name = [aGroup valueForKeyPath:@"leader.name"];

keyPath称为键路径,用“.”号连接键。同样有两种方法:

- (id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;

备注:keyPath版本方法更灵活,而key版本稍微快一些。如果键是通过参数传给我,通常用vauleForKeyPath:,如果键是硬编码的,那我通常会用valueForKey:

KVO(key-value observing)

概要

KVO某个对象的属性改变时通知其它对象的机制。例如MVC中,model层数据变化便可以通过KVO,告知Controller。Cocoa有若干观察机制,包括委托和NSNotification,但是KVO的开销更小。

注意:只有在使用键值编码准则来访问访问器或实例变量的情况下,才可以监视属性的变化。在方法内直接改变实例变量变量值时,就不能监视了。具体KVC准则有三点:

  1. 随访问器方法而改变
  2. 使用setValue:forKey:和键进行改变。此时可能不经由访问器。
  3. 使用setValue:forKeyPath:和键路径进行改变。此时也可能不经由访问器。

基本处理

假设现在要在viewController观察一个model中name的变化,分为三个步骤,代码如下

  1. 首先注册KVO

     [self addObserver:self //观察者
            forKeyPath:@"model.name" //被观察的属性
               options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld //指定属性变化时发送的通知消息中包含变化内容的字典数据中包含什么样的值
        //NSKeyValueObservingOptionNew提供属性改变后的值
        //NSKeyValueObservingOptionOld提供属性改变前的值
               context:nil];//context见后面KVO与context解释,可以为nil
    
  2. 指定监视的属性被改变时,要实现的方法

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
    

{
//keyPath:被检测属性
//object:keyPath所属对象
//change:显示变化内容的字典数据,可按如下方式获得具体值
//id old = [change objectForKey:NSKeyValueChangeOldKey];
//id new = [change objectForKey:NSKeyValueChangeNewKey];
//context:返回注册观察者时指定的值。
}
```

  1. 要停止已经注册的监视,可使用如下方法

    [self removeObserver:self forKeyPath:@"model.name"];
    

进阶KVO

  • KVO与context
    如果我们观察很多个键的话,在实现一个类的时候,把它自己注册成为观察者的话,一个非常重要的点是要传入这个类唯一的context,推荐把以下代码

     static int const PrivateKVOContext;
    

写在这个类.m文件的顶端,然后我们像这样调用 API 并传入 PrivateKVOContext的指针:

```
[otherObject addObserver:self forKeyPath:@"someKey" options:someOptions context:&PrivateKVOContext];
```

然后我们这样写 -observeValueForKeyPath:...的方法

```
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ 
if (context == &PrivateKVOContext) { 
    // 这里写相关的观察代码 
} else { 
    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 
}

}
```
这将确保我们写的子类都是正确的。如此一来,子类和父类都能安全的观察同样的键值而不会冲突。否则我们将会碰到难以 debug 的奇怪行为。

  • NSKeyValueObservingOptionInitial
    我们需要在一个值改变时更新UI,也需要在第一次运行代码的时候更新UI。我们可以在-addObserver:forKeyPath:...时,在option中增加NSKeyValueObservingOptionInitial来设置这件事,使得KVO在调用-addObserver:forKeyPath:...时也被触发一次。

  • NSKeyValueObservingOptionPrior
    注册KVO时,可以添加NSKeyValueObservingOptionPrior选项,当KVO被触发后,我们会收到两个通知:值改变前和值改变后。变更前的通知将会在 change字典中有不同的键。可以像下面这样区分通知是在变更之前还是之后:

     if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
        // 改变之前
     } else { 
       // 改变之后
     }
    

设置属性间的依赖

假如有个Person类,类里有三个属性,fullName、firstName、lastName。按照之前的知识,如果需要观察名字的变化,就要分别添加fullName、firstName、lastName三次观察,非常麻烦。如果能够只观察 fullName,并建立fullNamefirstName、lastName的某种依赖关系,当发生变化时,也受到通知,那该多好啊!
可以参考如下文章:

KVO的权衡

  • KVO的一个不足是不能对属性名进行编译检查。
  • 只有一个回调方法,这意味着开发者经常会在那个地方高出一堆不相关的代码。
  • 当你不是某个路径的观察者而调用removeObserver时会发生崩溃。

KVO&KVC相关问题

  1. 怎样手动触发KVO?
    答案
  2. KVO实现原理
    答案
  3. KVC和KVO的keyPath一定是属性么?
    KVO支持实例变量。
  4. KVC的keyPath中的集合运算符如何使用?
    答案

参考文章:

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

推荐阅读更多精彩内容