KVC设值和取值

KVC(Key-Value Coding)键值编码

苹果官方文档地址:https://developer.apple.com/library/archive/navigation/

KVC是利用NSKeyValueCoding 非正式协议实现的一种机制,在开发中可以通过 key 直接访问对象的属性,或者对属性进行赋值操作,而不需要调用明确的存取方法。它允许我们在运行时动态地访问和修改对象的属性,而不是在编译时决定。

一、KVC原理分析

1、设值流程分析
### Search Pattern for the Basic Setter

The default implementation of `setValue:forKey:`, given `key` and `value` parameters as input, attempts to set a property named `key` to `value` (or, for non-object properties, the unwrapped version of  `value`, as described in [Representing Non-Object Values](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/DataTypes.html#//apple_ref/doc/uid/20002171-BAJEAIEE)) inside the object receiving the call, using the following procedure:

1.  Look for the first accessor named `set<Key>:` or `_set<Key>`, in that order. If found, invoke it with the input value (or unwrapped value, as needed) and finish.

2.  If no simple accessor is found, and if the class method `accessInstanceVariablesDirectly` returns `YES`, look for an instance variable with a name like `_<key>`, `_is<Key>`, `<key>`, or `is<Key>`, in that order. If found, set the variable directly with the input value (or unwrapped value) and finish.

3.  Upon finding no accessor or instance variable, invoke `setValue:forUndefinedKey:`. This raises an exception by default, but a subclass of `NSObject` may provide key-specific behavior.

设置值setValue: forKey:之后,底层的操作如下:

1、底层会优先调用set<Key>_set<Key>方法,按照这个先后顺序,且只实现其中一个方法;
2、如果没有上述方法,就会调用accessInstanceVariablesDirectly进行判断,如果返回NO,直接进入下面3步骤,否则YES,调用实例变量赋值顺序_<key>, _is<Key>, <key>, is<Key>,且只实现其中一个方法;
3、如果没有找到存取方法或者实例变量就会调用setValue:forUndefinedKey:方法。

2、取值流程分析
### Search Pattern for the Basic Getter

The default implementation of `valueForKey:`, given a `key` parameter as input, carries out the following procedure, operating from within the class instance receiving the `valueForKey:` call.

1.  Search the instance for the first accessor method found with a name like `get<Key>`, `<key>`, `is<Key>`, or `_<key>`, in that order. If found, invoke it and proceed to step 5 with the result. Otherwise proceed to the next step.

2.  If no simple accessor method is found, search the instance for methods whose names match the patterns `countOf<Key>` and `objectIn<Key>AtIndex:` (corresponding to the primitive methods defined by the `NSArray` class) and `<key>AtIndexes:` (corresponding to the `[NSArray](https://developer.apple.com/library/archive/documentation/LegacyTechnologies/WebObjects/WebObjects_3.5/Reference/Frameworks/ObjC/Foundation/Classes/NSArrayClassCluster/Description.html#//apple_ref/occ/cl/NSArray)` method `objectsAtIndexes:`).

    If the first of these and at least one of the other two is found, create a collection proxy object that responds to all `NSArray` methods and return that. Otherwise, proceed to step 3.

    The proxy object subsequently converts any `NSArray` messages it receives to some combination of `countOf<Key>`, `objectIn<Key>AtIndex:`, and `<key>AtIndexes:` messages to the key-value coding compliant object that created it. If the original object also implements an optional method with a name like `get<Key>:range:`, the proxy object uses that as well, when appropriate. In effect, the proxy object working together with the key-value coding compliant object allows the underlying property to behave as if it were an `NSArray`, even if it is not.

3.  If no simple accessor method or group of array access methods is found, look for a triple of methods named `countOf<Key>`, `enumeratorOf<Key>`, and `memberOf<Key>:` (corresponding to the primitive methods defined by the `[NSSet](https://developer.apple.com/library/archive/documentation/LegacyTechnologies/WebObjects/WebObjects_3.5/Reference/Frameworks/ObjC/Foundation/Classes/NSSetClassCluster/Description.html#//apple_ref/occ/cl/NSSet)` class).

    If all three methods are found, create a collection proxy object that responds to all `NSSet` methods and return that. Otherwise, proceed to step 4.

    This proxy object subsequently converts any `NSSet` message it receives into some combination of `countOf<Key>`, `enumeratorOf<Key>`, and `memberOf<Key>:` messages to the object that created it. In effect, the proxy object working together with the key-value coding compliant object allows the underlying property to behave as if it were an `NSSet`, even if it is not.

4.  If no simple accessor method or group of collection access methods is found, and if the receiver's class method `[accessInstanceVariablesDirectly](https://developer.apple.com/library/archive/documentation/LegacyTechnologies/WebObjects/WebObjects_3.5/Reference/Frameworks/ObjC/EOF/EOControl/Classes/NSObjectAdditions/Description.html#//apple_ref/occ/clm/NSObject/accessInstanceVariablesDirectly)` returns `YES`, search for an instance variable named `_<key>`, `_is<Key>`, `<key>`, or `is<Key>`, in that order. If found, directly obtain the value of the instance variable and proceed to step 5\. Otherwise, proceed to step 6.

5.  If the retrieved property value is an object pointer, simply return the result.

    If the value is a scalar type supported by `NSNumber`, store it in an `NSNumber` instance and return that.

    If the result is a scalar type not supported by NSNumber, convert to an `NSValue` object and return that.

6.  If all else fails, invoke `[valueForUndefinedKey:](https://developer.apple.com/documentation/objectivec/nsobject/1413457-value)`. This raises an exception by default, but a subclass of `NSObject` may provide key-specific behavior.

输入valueForKey:之后,底层的操作如下:

1、依次查找方法 get<key><key>is<key>_<key> ,如果能找到,进行5步骤,否则进行下一步;
2、如果没有上面的方法,就去查找countOf<Key>, objectIn<Key>AtIndex:, <key>AtIndexes:的方法,看是否是属于NSArray;
3、如果没有找到属于NSArray,就看countOf<Key>,enumeratorOf<Key>,memberOf<Key>:是否是属于NSSet;
4、如果上面方法都不存在,就查找类方法accessInstanceVariablesDirectly,如果返回为NO,就直接进入6步骤,否则YES, 依次显示实例变量的值_<key>,_is<Key>,<key>,is<Key>,且只显示一个,然后进行下一步;
5、如果获取到的属性值是一个对象指针,直接返回结果;如果该值是支持NSNumber类型,返回NSNumber类型实例;如果该值不支持NSNumber类型,返回NSValue对象;
6、如果以上步骤都不是,就调用valueForUndefinedKey:方法报错提醒。

二、赋值为空判断个例

@interface TestKVC : NSObject
@property (nonatomic, assign) int age;
@property (nonatomic, copy) NSString *subject;
@end
@implementation TestKVC
- (void)setNilValueForKey:(NSString *)key{
    NSLog(@"key:%@是空值",key);
}

@end
//此处省略
........
TestKVC *test = [TestKVC alloc];
    [test setValue:nil forKey:@"age"];
    [test setValue:nil forKey:@"subject"];

打印结果:key:age是空值,subject对象并不会打印

/* Given that an invocation of -setValue:forKey: would be unable to set the keyed value because the type of the parameter of the corresponding accessor method is an NSNumber scalar type or NSValue structure type but the value is nil, set the keyed value using some other mechanism. The default implementation of this method raises an NSInvalidArgumentException. You can override it to map nil values to something meaningful in the context of your application.
*/
- (void)setNilValueForKey:(NSString *)key;

查找方法注释了解到,空值的打印只支持NSNumberstructure.

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

推荐阅读更多精彩内容