iOS - KVC

[toc]

参考

KVC

KVO

http://www.jianshu.com/p/fbd1e7c93fd0

KVC

KVC (Key Value Coding 键值编码) 是一种可以通过字符串 (key) 来间接访问类属性的机制, 而不是通过直接调用 Setter、Getter 方法访问。

KVC 支持类对象和内建基本数据类型。

很多情况下可以简化程序代码。

KVC API

NSKeyValueCoding.h

KVC 关键方法定义在 NSKeyValueCoding.h

给NSObject添加的分类
@interface NSObject(NSKeyValueCoding)

@property (class, readonly) BOOL accessInstanceVariablesDirectly;

- (nullable id)valueForKey:(NSString *)key;

- (BOOL)validateValue:(inout id _Nullable * _Nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;

- (nullable id)valueForKeyPath:(NSString *)keyPath;

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

- (BOOL)validateValue:(inout id _Nullable * _Nonnull)ioValue forKeyPath:(NSString *)inKeyPath error:(out NSError **)outError;

- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;

- (NSMutableOrderedSet *)mutableOrderedSetValueForKeyPath:(NSString *)keyPath API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));

- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;

- (nullable id)valueForUndefinedKey:(NSString *)key;

- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

- (void)setNilValueForKey:(NSString *)key;

- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;

- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;

@end
给 NSArray、NSDictionary、NSMutableDictionary、NSOrderedSet、NSSet 添加的分类
@interface NSArray<ObjectType>(NSKeyValueCoding)

- (id)valueForKey:(NSString *)key;

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

@end

  
@interface NSDictionary<KeyType, ObjectType>(NSKeyValueCoding)

- (nullable ObjectType)valueForKey:(NSString *)key;

@end

  
@interface NSMutableDictionary<KeyType, ObjectType>(NSKeyValueCoding)

- (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;

@end

  
@interface NSOrderedSet<ObjectType>(NSKeyValueCoding)

- (id)valueForKey:(NSString *)key API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));

- (void)setValue:(nullable id)value forKey:(NSString *)key API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));

@end

  
@interface NSSet<ObjectType>(NSKeyValueCoding)

- (id)valueForKey:(NSString *)key;

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

@end

使用方法

获取值
valueForKey: // 传入NSString属性的名字。
valueForKeyPath: // 属性的路径, xx.xx
valueForUndefinedKey:  // 默认实现是抛出异常, 可重写这个函数做错误处理
修改值
setValue:forKey: // 为对象的属性赋值, value的值必须是id,也就是说不能传基本数据类型, 必须是指针类型的变量
setValue:forKeyPath: // 包含了setValue:forKey:的功能, 并且还可为对象内的对象的属性赋值
setValuesForKeysWithDictionary: // 字典转模型, 对模型进行一次性赋值
注意点:
底层还是调用了setValue:forKey:
字典转模型的时候, 字典中的某一个key一定要在模型中有对应的属性
如果一个模型中包含了另外的模型对象, 是不能直接转化成功的。
通过KVC转化模型中的模型, 也是不能直接转化成功的
异常
setValue:forUnderfinedKey:  // 默认实现是抛出异常, 可重写这个函数做错误处理
setNilValueForKey:  // 对非类对象属性设置nil时调用, 默认抛出异常。
valueForUndefinedKey: // 访问了不存在的key

底层实现

KVC 实现分析:

[site setValue:@"sitename" forKey:@"name"];

// 上面的KVC代码会被编译器处理成下面三行:
SEL sel = sel_get_uid(setValue:forKey);
IMP method = objc_msg_loopup(site->isa, sel);
method(site, sel, @"sitename", @"name");

一个对象在调用setValue时:

  1. 首先根据方法名setValue:forKey找到运行方法所需要的SEL。

  2. 再根据对象的isa指针, 结合SEL, 找到具体的方法实现IMP。

  3. 调用方法实现完成KVC赋值


搜索方式

如何访问属性值

setValue:forKey: 搜索方式
image

注: accessInstanceVariablesDirectly 该类方法的默认返回值为YES

  1. 首先按顺序搜索setKey:_setKey: 方法(key指成员变量名, 首字母大写); 若找到即调用。

  2. 若上面2个setter方法都没找到, 如果类方法 accessInstanceVariablesDirectly 返回YES。那么按 _key_isKeykeyiskey 的顺序搜索成员名。

  3. 如果没有找到成员变量, 调用 setValue:forUnderfinedKey: 方法的默认抛出异常, 我们可以根据需要重写它


valueForKey: 搜索方式
image
  1. 首先按 getKeykeyisKeykey 的顺序查找getter方法, 找到直接调用。如果是 BOOLint 等内建值类型, 会做NSNumber的转换。
  2. 若上面的getter没找到, 查找 countOfKeyobjectInKeyAtindexKeyAtindexes 格式的方法。如果countOfKey和另外两个方法中的一个找到, 那么就会返回一个可以响应NSArray所有方法的代理集合的NSArray消息方法。
  3. 还没找到, 查找 countOfKeyenumeratorOfKeymemberOfKey 格式的方法。如果这三个方法都找到, 那么就返回一个可以响应NSSet所有方法的代理集合。
  4. 还是没找到, 如果类方法 accessInstanceVariablesDirectly 返回YES。那么按 _key_isKeykeyiskey 的顺序搜索成员名。
  5. 再没找到, 调用 valueForUndefinedKey。如果这个方法还是没有被实现的话, 程序会抛出一个 NSUndefinedKeyException 异常错误。

KVC 在某种程度上提供了访问器的替代方案。因为访问器方法很优秀, 所以 KVC 尽可能使用访问器方法。


KVC 触发 KVO

KVC 触发 KVO 不依赖于setter方法

通过 KVC 修改成员变量, 即便没有 setter 方法, 依然能够触发 KVO。

KVC 内部会通知 key 发生了改变, 猜测是调用了 willChangeValueForKey:didChangeValueForKey: , 重写这两个方法并添加打印代码, 可以看到kVC赋值时, 确实有被调用。

其中, didChangeValueForKey: 内部会触发KVO的监听回调方法 observeValueForKeyPath:ofObject:change:context:, 完成KVO监听。具体参考《KVO》。

面试题

KVC 和 object_setIvar
为什么 KVC 可以用 NSNumber 来接收 int、float 的数据类型?
- (void)setValue:(nullable id)value forKey:(NSString *)key;
- (nullable id)valueForKey:(NSString *)key;
  • 使用 valueForKey: 时, KVC会自动将标量值 (intfloat等) 翻入NSNumberNSValue 中包装成一个对象, 然后返回。也就是说, KVC有自动包装功能。

  • 而使用 setValue:forKey: 是相当于自动调用了 NSNumber 的属性 intValue/floatValue, 取出具体值赋值给成员变量; 所以 KVC 可以直接将 NSNumber 赋值给基本数据类型的成员变量。


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