KVO和KVC

KVC

  • kvc全称key-value-coding(键值编码),通常是用来给某一个对象的属性进行赋值,比如有一个person类,其对外有3个属性 —— 姓名、性别和年龄,我们创建一个人 p 后可以通过点语法直接给p的属性赋值。
// 创建person对象
Person *p = [[Person alloc] init];
// 给person对象属性赋值
p.name = @"狗蛋儿";
p.sex = @"男";
p.age = 188;

  • 接着我们通过kvc的方式给 p 对象的属性赋值

    • 注意:因为setValue中的值是id类型,指向任何对象,所以需要将整数包装成一个对象


    [p setValue:@"狗蛋儿" forKey:@"name"];
    [p setValue:@"男" forKey:@"sex"];
    [p setValue:@188 forKey:@"age"];
    
    
  • 到这里可能有的朋友会说这样赋值麻烦而且多此一举,但是如果person这个类中有个私有的属性 —— height,只提供输出接口,那么正常情况下我们是无法改变其值的,如下:

.h

@interface Person : NSObject

/**
 * 获取身高
 */
- (void)getHeight;

@end

.m

@implementation Person
{
    NSInteger _height;  // 身高
}

- (void)getHeight {

    NSLog(@"身高--%ld", _height);
}

@end

  • 这个时候外界是无法访问到 _height 这个属性的,但是如果想改变其值,那么我们就可以使用KVC的方式

[p setValue:@1.8f forKey:@"height"];

// 获取身高
[p getHeight];

结果:


身高打印结果.png
  • 除了 setValue:forKey: 这个方法外,苹果还提供了另一个方法 setValue:forKeyPath: 这两个方法对于普通属性来说是没有区别的,都可用,但是对于一些特殊的属性自然就要使用 setValue:forKeyPath: 方法了(看名字都这么长,肯定比较牛)至于牛仔哪里,肯定要试一试

    • 假如person这个类中我们又有个属性dog,Dog类中又有个属性体重,那么我们怎么通过'p'这个对象去设置狗的属性呢?
        // 初始化Dog对象
    p.dog = [[Dog alloc] init];
    // 给dog对象赋值
    [p setValue:@"旺财" forKey:@"dog.name"];
    
    

    结果:


    逐级寻找key错误演示.png
    • 从图中可以看到,如果我们使用 setValue:forKey: 这个方法,Xcode会报错说找不到dog.name这个key,想想我们在stroyboard中,如果我们控件连线出现错误,也会报相似的错误,说明了stroyboard在赋值的时候也是通过kvc的方式来操作的
    • 现在我们来试试用 setValue:forKeyPath: 方法


    [p setValue:@"旺财" forKeyPath:@"dog.name"];
        
    

    结果:


    逐级寻找key演示.png
    • 成功了,说明 setValue:forKeyPath: 方法中包含了 setValue:forKeyPath: 的方法,但是内部增加了更高级的功能 —— 内部实现:它会先去 person类中寻找有没有 dog这个key,如果有,那么会去Dog类中寻找有没有 name这个key,如果有,就给name这个key赋值,所以个人比较喜欢使用 setValue:forKeyPath这个方法
  • 不知道大家有没有注意到,前面我们使用KVC的方式给 _height 属性赋值的时候我们是这样写的

[p setValue:@1.8f forKey:@"height"];

  • 这边有个问题 —— 我们传入的字符串key是 height,但是定义的属性则是 _height,为什么还可以给_height 赋值呢?

    • 这说明使用KVC对某个属性进行赋值时,可以不用加 _,因为KVC的查找规则是:先寻找和直接输出的字符串相同的成员变量,如果找不到再去寻找以_开头的相似的成员变量
  • 当然,KVC的用处不仅仅这点,开发中我们经常需要将字典转成模型以方便View取值,这里我们给Person 类再提供一个外部方法,在方法中实现使用KVC的方式将传入的字典转成模型

    • 当然,setValuesForKeysDictionary:这个方法只能实现比较简单的字典转模型,如果是深层次的字典,还是需要手动去实现,也可以使用第三方框架来处理(业内用的最多的应该就是MJExtension,不得不说确实好用)
- (instancetype)initWithDict:(NSDictionary *)dict {

    if (self = [super init]) {
        
        [self setValuesForKeysWithDictionary:dict];
    }
    
    return self;
}

  • 接着我们在外部调用 initWithDict: 这个方法来实现字典转模型
// 初始化字典内容
    NSDictionary *dict = @{
                           @"name" : @"狗蛋儿",
                           @"sex" : @"男",
                           @"age" : @188,
                           };
    // 调用initWithDict:方法
    Person *p1 = [p initWithDict:dict];
    // 打印
    NSLog(@"p1.name=%@, p1.sex=%@, p1.age=%ld", p1.name, p1.sex, p1.age);

结果:


KVC字典转模型演示.png
  • 前面只提到赋值,但是怎么取值呢,其实很简单,苹果提供了2个取值的方法,取值时只需要将对应的key传入就可以了

    • valueForKey:
    • valueForKeyPath:
  • 总结:

    • KVC常见的2种应用场景
      • 对私有变量进行赋值
      • 字典转模型
    • 字典转模型注意点
      • 字典中的某个key一定要在模型中有对应属性
      • 一个模型中如果包含了另外的模型对象,是无法直接使用setValuesForKeysDictionary:方法转化成功的
      • 通过KVC转化模型中你的模型,也是无法直接转化成功的

KVO

  • KVO全称key-value-observing,也就是我们常说的观察者模式,它的原理就是利用一个key来找到某个属性并监听值的改变
  • KVO的使用比较简单,大致分为3个步骤
    • 添加观察者
    • 在观察者中实现监听方法
    • 移除观察者


1.添加观察者

    // 实例化person对象
    _p = [[Person alloc] init];
    _p.name = @"狗蛋儿";

    /**
     *  观察p对象的name值,如果改变则打印新值
     *
     *  @param observer 观察者
     *  @param keyPath  观察的属性
     *  @param options  观察模式
     *  @param context  额外数据
     */
     // options取值:NSKeyValueObservingOptionNew(新值), NSKeyValueObservingOptionOld(旧值)
     
    [_p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

2.在回调中实现监听方法

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    
    NSLog(@"%@的值改变为%@",keyPath, change[@"new"]);
}

3.移除观察者(重要)

- (void)dealloc {
    
    // 移除
    [_p removeObserver:self forKeyPath:@"name"];
}

结果:


KVO演示.png
  • 那么KVO实现原理:当观察一个对象时,一个新的类会动态被创建,这个类继承自该对象原本的类,并且重写了被观察的属性的setter方法,重写的setter方法会负责在调用原setter方法之前和之后,通知所有观察对象,最后把这个对象的isa指针指向这个新创建的子类,对象就变成新创建的子类的实例,苹果为了让我们认为这个类没有被修改过,还重写了-class方法(就是原本的类),至于为什么这样做,还没弄清楚,苹果也不希望暴露太多KVO实现细节,苹果官方文档只说明:被观察对象的isa指针会指向一个中间类,而不是原来真正的类

  • KVO的缺陷:KVO是很强大的,但是苹果给的API实在是不咋地,我们只能通过重写observeValueForKeyPath:ofObject:change:context:方法获得通知,想传block、自定义方法等都不行,所以实现开发中KVO使用的情景不多,更多是用Delegate(代理)或者NotificationCenter(通知中心)代替,当然很多大神已经吊打官方KVO好多次了,有想研究了可以去搜一下相关资料

最后附上本章参考Demo 密码: b55p

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

推荐阅读更多精彩内容

  • KVC 与 KVO 无疑是 Cocoa 提供给我们的一个非常强大的特性,使用熟练可以让我们的代码变得非常简洁并且易...
    面包与世界阅读 748评论 0 3
  • 一,概述 KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就...
    HZX大雄阅读 204评论 0 0
  • KCV 其实由于ObjC的语言特性,你根部不必进行任何操作就可以进行属性的动态读写,这种方式就是Key Value...
    TYM阅读 1,053评论 0 4
  • 前言: 最近顺丰小哥事件引发大众关注,这一事件的后续发酵似乎对所有管理者提出了更高层次的要求,而更重要的是掀起了大...
    fltmyxq阅读 3,012评论 1 10
  • To all of my girl friends, 难得有闲情逸致能够静下心里写篇Blog,好久没执笔了,说的不...
    三小柔阅读 1,548评论 0 0