KVC 底層原理探索

定義

KVC的全稱是Key-Value Coding淤击,翻譯成中文是 鍵值編碼际长,鍵值編碼是由NSKeyValueCoding非正式協(xié)議啟用的一種機(jī)制铃剔,對(duì)象采用該協(xié)議來(lái)間接訪問(wèn)其屬性费变。既可以通過(guò)一個(gè)字符串key來(lái)訪問(wèn)某個(gè)屬性粥帚。這種間接訪問(wèn)機(jī)制補(bǔ)充了實(shí)例變量及其相關(guān)的訪問(wèn)器方法所提供的直接訪問(wèn)灰蛙。

KVC 相關(guān)API

常用方法

主要有以下四個(gè)常用的方法

  • 通過(guò)key 設(shè)值/取值
//直接通過(guò)Key來(lái)取值
- (nullable id)valueForKey:(NSString *)key;

//通過(guò)Key來(lái)設(shè)值
- (void)setValue:(nullable id)value forKey:(NSString *)key;

  • 通過(guò)keyPath (即路由)設(shè)值/取值
//通過(guò)KeyPath來(lái)取值
- (nullable id)valueForKeyPath:(NSString *)keyPath; 

//通過(guò)KeyPath來(lái)設(shè)值                 
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  

其他方法

//默認(rèn)返回YES祟剔,表示如果沒(méi)有找到Set<Key>方法的話,會(huì)按照_key缕允,_iskey峡扩,key,iskey的順序搜索成員障本,設(shè)置成NO就不這樣搜索
+ (BOOL)accessInstanceVariablesDirectly;

//KVC提供屬性值正確性驗(yàn)證的API教届,它可以用來(lái)檢查set的值是否正確、為不正確的值做一個(gè)替換值或者拒絕設(shè)置新值并返回錯(cuò)誤原因驾霜。
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

//這是集合操作的API案训,里面還有一系列這樣的API,如果屬性是一個(gè)NSMutableArray粪糙,那么可以用這個(gè)方法來(lái)返回强霎。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

//如果Key不存在,且KVC無(wú)法搜索到任何和Key有關(guān)的字段或者屬性蓉冈,則會(huì)調(diào)用這個(gè)方法城舞,默認(rèn)是拋出異常。
- (nullable id)valueForUndefinedKey:(NSString *)key;

//和上一個(gè)方法一樣寞酿,但這個(gè)方法是設(shè)值家夺。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

//如果你在SetValue方法時(shí)面給Value傳nil,則會(huì)調(diào)用這個(gè)方法
- (void)setNilValueForKey:(NSString *)key;

//輸入一組key,返回該組key對(duì)應(yīng)的Value伐弹,再轉(zhuǎn)成字典返回拉馋,用于將Model轉(zhuǎn)到字典。
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;

KVC 設(shè)值 底層原理

在日常開(kāi)發(fā)中惨好,針對(duì)對(duì)象屬性的賦值煌茴,一般有以下兩種方式

  • 直接通過(guò)setter方法賦值
  • 通過(guò)KVC鍵值編碼賦值
ZGPerson *person = [[ZGPerson alloc] init];
// 1、一般setter 方法
person.name      = @"張三";
// 2日川、KVC方式
[person setValue:@"李四" forKey:@"name"]; 

下面針對(duì)使用最多的KVC設(shè)值方法:setValue:forKey蔓腐,來(lái)進(jìn)行其底層原理的探索。

首先進(jìn)入setValue:forKey的聲明逗鸣,發(fā)現(xiàn)是在Foundation框架中合住,而Foundation框架是不開(kāi)源的绰精,所以我們通過(guò)Key-Value Coding Programming Guide蘋果官方文檔來(lái)研究

KVC 設(shè)值setValue

當(dāng)調(diào)用setValue:forKey:設(shè)置屬性value時(shí),其底層的執(zhí)行流程為

  • 【第一步】首先查找是否有這三種setter方法透葛,按照查找順序?yàn)?code>set<Key>:-> _set<Key> -> setIs<Key>

    • 如果有其中任意一個(gè)setter方法笨使,則直接設(shè)置屬性的value(主注意:key是指成員變量名,首字符大小寫需要符合KVC的命名規(guī)范)

    • 如果都沒(méi)有僚害,則進(jìn)入【第二步】

  • 【第二步】:如果沒(méi)有第一步中的三個(gè)簡(jiǎn)單的setter方法硫椰,則查找accessInstanceVariablesDirectly是否返回YES

    • 如果返回YES萨蚕,則查找間接訪問(wèn)的實(shí)例變量進(jìn)行賦值靶草,查找順序?yàn)椋?code>_<key> -> _is<Key> -> <key> -> is<Key>

      • 如果找到其中任意一個(gè)實(shí)例變量,則賦值

      • 如果都沒(méi)有岳遥,則進(jìn)入【第三步】

    • 如果返回NO奕翔,則進(jìn)入【第三步】

  • 【第三步】如果setter方法 或者 實(shí)例變量都沒(méi)有找到,系統(tǒng)會(huì)執(zhí)行該對(duì)象的setValue:forUndefinedKey:方法浩蓉,默認(rèn)拋出NSUndefinedKeyException類型的異常

綜上所述派继,KVC通過(guò) setValue:forKey: 方法設(shè)值的流程以設(shè)置LGPerson的對(duì)象person的屬性name為例,如下圖所示

KVC 設(shè)值
KVC 取值 底層原理

同樣的捻艳,我們可以通過(guò)官方文檔分析KVC取值的底層原理

當(dāng)調(diào)用valueForKey:時(shí)驾窟,其底層的執(zhí)行流程如下

  • 【第一步】首先查找getter方法,按照get<Key> -> <key> -> is<Key> -> _<key>的方法順序查找认轨,

    • 如果找到绅络,則進(jìn)入【第五步】

    • 如果沒(méi)有找到,則進(jìn)入【第二步】

  • 【第二步】如果第一步中的getter方法沒(méi)有找到嘁字,KVC會(huì)查找countOf <Key>和objectIn <Key> AtIndex :和<key> AtIndexes :

    • 如果找到countOf <Key>和其他兩個(gè)中的一個(gè)恩急,則會(huì)創(chuàng)建一個(gè)響應(yīng)所有NSArray方法的集合代理對(duì)象,并返回該對(duì)象纪蜒,即NSKeyValueArray假栓,是NSArray子類。代理對(duì)象隨后將接收到的所有NSArray消息轉(zhuǎn)換為countOf<Key>霍掺,objectIn<Key> AtIndex:和<key>AtIndexes:消息的某種組合,用來(lái)創(chuàng)建鍵值編碼對(duì)象拌蜘。如果原始對(duì)象還實(shí)現(xiàn)了一個(gè)名為get<Key>:range:之類的可選方法杆烁,則代理對(duì)象也將在適當(dāng)時(shí)使用該方法(注意:方法名的命名規(guī)則要符合KVC的標(biāo)準(zhǔn)命名方法,包括方法簽名简卧。)

    • 如果沒(méi)有找到這三個(gè)訪問(wèn)數(shù)組的兔魂,請(qǐng)繼續(xù)進(jìn)入【第三步】

  • 【第三步】如果沒(méi)有找到上面的幾種方法,則會(huì)同時(shí)查找countOf <Key>举娩,enumeratorOf<Key>和memberOf<Key>這三個(gè)方法

    • 如果這三個(gè)方法都找到析校,則會(huì)創(chuàng)建一個(gè)響應(yīng)所有NSSet方法的集合代理對(duì)象构罗,并返回該對(duì)象,此代理對(duì)象隨后將其收到的所有NSSet消息轉(zhuǎn)換為countOf<Key>智玻,enumeratorOf<Key>和memberOf<Key>:消息的某種組合遂唧,用于創(chuàng)建它的對(duì)象

    • 如果還是沒(méi)有找到,則進(jìn)入【第四步】

  • 【第四步】如果還沒(méi)有找到吊奢,檢查類方法InstanceVariablesDirectly是否YES盖彭,依次搜索_<key>,_is<Key>页滚,<key>或is<Key>的實(shí)例變量

    • 如果搜到召边,直接獲取實(shí)例變量的值,進(jìn)入【第五步】
  • 【第五步】根據(jù)搜索到的屬性值的類型裹驰,返回不同的結(jié)果

    • 如果是對(duì)象指針隧熙,則直接返回結(jié)果

    • 如果是NSNumber支持的標(biāo)量類型,則將其存儲(chǔ)在NSNumber實(shí)例中并返回它

    • 如果是是NSNumber不支持的標(biāo)量類型幻林,請(qǐng)轉(zhuǎn)換為NSValue對(duì)象并返回該對(duì)象

  • 【第六步】如果上面5步的方法均失敗贞盯,系統(tǒng)會(huì)執(zhí)行該對(duì)象的valueForUndefinedKey:方法,默認(rèn)拋出NSUndefinedKeyException類型的異常

綜上所述滋将,KVC通過(guò) valueForKey: 方法取值的流程以設(shè)置LGPerson的對(duì)象person的屬性name為例邻悬,如下圖所示

KVC 取值

使用keyPath

在實(shí)際開(kāi)發(fā)過(guò)程中,一個(gè)類的成員變量有可能是自定義類或者其他的復(fù)雜數(shù)據(jù)類型随闽,我們可以先用KVC獲取該屬性父丰,然后再用KVC來(lái)獲取這個(gè)自定義類的屬性。但這樣比較繁瑣掘宪,因此KVC提供了一個(gè)解決方案蛾扇,keyPath

// 通過(guò)KeyPath來(lái)取值
- (nullable id)valueForKeyPath:(NSString *)keyPath;
// 通過(guò)KeyPath來(lái)設(shè)值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

下面舉個(gè)例子

@interface ZGPerson : NSObject
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger *sex;
@property (strong, nonatomic) NSNumber *age;
@property (strong, nonatomic) Address *address;
@end
......分割線
@interface Address : NSObject
@property (copy, nonatomic) NSString *city;
@property (copy, nonatomic) NSString *street;
@end
......分割線
- (void)viewDidLoad {
    [super viewDidLoad];
    ZGPerson *myself = [[ZGPerson alloc] init];

    [myself setValue:@"ysy" forKey:@"name"];

    NSLog(@"-------name = %@",myself.name);
    NSLog(@"-------name = %@",[myself valueForKey:@"name"]);

    /**
     keyPath的setter方法:setValue: forKeyPath:
     keyPath的getter方法:valueForKeyPath:

     keyPath為多級(jí)訪問(wèn)魏滚,使用點(diǎn)語(yǔ)法
     */

    //注意镀首,這里要想使用keypath對(duì)adress的屬性進(jìn)行賦值,必須先給myself賦一個(gè)Address對(duì)象
    Address *myAddress = [[Address alloc] init];

    [myself setValue:myAddress forKey:@"address"];

    //KeyPath為多級(jí)訪問(wèn)
    [myself setValue:@"shanghai" forKeyPath:@"address.city"];

    NSLog(@"-------city = %@",myself.address.city);

    NSLog(@"-------city = %@",[myself valueForKeyPath:@"address.city"]);
    
}

異常處理

使用KVC過(guò)程中最常見(jiàn)的異常就是不小心使用了錯(cuò)誤的key鼠次,或者在設(shè)值時(shí)不小心傳了nil的值更哄,KVC有特定的方法處理這些異常。

  • KVC處理nil異常腥寇,如果在設(shè)值過(guò)程中成翩,不小心傳了nil值,KVC會(huì)調(diào)用方法 setNilValueForKey:赦役,這個(gè)默認(rèn)方法是拋出 NSInvalidArgumentException 異常麻敌,所以一般而言最好重寫這個(gè)方法,對(duì)異常進(jìn)行處理掂摔。
  • KVC處理UndefinedKey異常术羔,如果在設(shè)值取值傳的key不存在時(shí)赢赊,程序就會(huì)crash,設(shè)值會(huì)調(diào)用到 setValue:forUndefinedKey:方法级历,而取值會(huì)調(diào)用valueForUndefinedKey:方法释移,這兩個(gè)方法默認(rèn)都是拋出 NSUndefinedKeyException異常,因此如果要避免程序crash鱼喉,可以重寫這兩個(gè)方法秀鞭。

KVC 的多值操作

批量取值操作

KVC還有更強(qiáng)大的功能,可以根據(jù)給定的一組key扛禽,獲取到一組value锋边,并且以字典的形式返回。

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

批量賦值操作

同樣编曼,也可以通過(guò)KVC進(jìn)行批量賦值豆巨。使用對(duì)象調(diào)用setValuesForKeysWithDictionary:方法時(shí),可以傳入一個(gè)包含key掐场、value的字典進(jìn)去往扔,KVC可以將所有數(shù)據(jù)按照屬性名和字典的key進(jìn)行匹配,并將value給User對(duì)象的屬性賦值熊户。

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

示例

示例如下:

//批量賦值
NSDictionary *dic = @{
                      @"name":@"xiaoMing",
                      @"sex":@1,
                      @"age":@12,
                      @"address":myAddress
                      };

[myself setValuesForKeysWithDictionary:dic];

//批量取值
NSArray *keys = @[@"name",@"age",@"sex",@"address"];

NSDictionary *values = [myself dictionaryWithValuesForKeys:keys];

NSLog(@"%@",values);

輸出:

{
    address = "<Address: 0x280d50360>";
    age = 12;
    name = xiaoMing;
    sex = 1;
}

集合屬性操作

當(dāng)我們要操作一個(gè)對(duì)象里的集合屬性時(shí)(NSArray萍膛、NSSet等),我們可以通過(guò)KVC方法取到集合屬性嚷堡,然后通過(guò)集合屬性操作集合中的元素蝗罗。實(shí)際開(kāi)發(fā)中最常用的方法,叫做間接操作蝌戒。

直接操作要實(shí)現(xiàn)下面的方法串塑,但通常不會(huì)用到:

有序集合對(duì)應(yīng)方法如下:

-countOf<Key>//必須實(shí)現(xiàn),對(duì)應(yīng)于NSArray的基本方法count:2  -objectIn<Key>AtIndex:

-<key>AtIndexes://這兩個(gè)必須實(shí)現(xiàn)一個(gè)北苟,對(duì)應(yīng)于 NSArray 的方法 objectAtIndex: 和 objectsAtIndexes:

-get<Key>:range://不是必須實(shí)現(xiàn)的桩匪,但實(shí)現(xiàn)后可以提高性能,其對(duì)應(yīng)于 NSArray 方法 getObjects:range:

-insertObject:in<Key>AtIndex:

-insert<Key>:atIndexes://兩個(gè)必須實(shí)現(xiàn)一個(gè)友鼻,類似于 NSMutableArray 的方法 insertObject:atIndex: 和 insertObjects:atIndexes:

-removeObjectFrom<Key>AtIndex:

-remove<Key>AtIndexes://兩個(gè)必須實(shí)現(xiàn)一個(gè)傻昙,類似于 NSMutableArray 的方法 removeObjectAtIndex: 和 removeObjectsAtIndexes:

-replaceObjectIn<Key>AtIndex:withObject:

-replace<Key>AtIndexes:with<Key>://可選的,如果在此類操作上有性能問(wèn)題彩扔,就需要考慮實(shí)現(xiàn)之

無(wú)序集合對(duì)應(yīng)方法如下:

-countOf<Key>//必須實(shí)現(xiàn)屋匕,對(duì)應(yīng)于NSArray的基本方法count:

-objectIn<Key>AtIndex:

-<key>AtIndexes://這兩個(gè)必須實(shí)現(xiàn)一個(gè),對(duì)應(yīng)于 NSArray 的方法 objectAtIndex: 和 objectsAtIndexes:

-get<Key>:range://不是必須實(shí)現(xiàn)的借杰,但實(shí)現(xiàn)后可以提高性能,其對(duì)應(yīng)于 NSArray 方法 getObjects:range:

-insertObject:in<Key>AtIndex:

-insert<Key>:atIndexes://兩個(gè)必須實(shí)現(xiàn)一個(gè)进泼,類似于 NSMutableArray 的方法 insertObject:atIndex: 和 insertObjects:atIndexes:

-removeObjectFrom<Key>AtIndex:

-remove<Key>AtIndexes://兩個(gè)必須實(shí)現(xiàn)一個(gè)蔗衡,類似于 NSMutableArray 的方法 removeObjectAtIndex: 和 removeObjectsAtIndexes:

-replaceObjectIn<Key>AtIndex:withObject:

-replace<Key>AtIndexes:with<Key>://這兩個(gè)都是可選的纤虽,如果在此類操作上有性能問(wèn)題,就需要考慮實(shí)現(xiàn)之

使用 KVC 進(jìn)行集合類的運(yùn)算

KVC 提供的valueForKeyPath:方法非常強(qiáng)大绞惦,可以在keyPath中嵌套集合運(yùn)算符對(duì)集合中的對(duì)象進(jìn)行相關(guān)的運(yùn)算逼纸,例如求一個(gè)數(shù)組中所有Person對(duì)象的age總和。集合對(duì)象主要指NSArray和NSSet济蝉,不包括NSDictionary杰刽。

集合運(yùn)算符的格式
keyPathToCollection.@collentionOperator.keyPathToproperty
  • keyPathToCollection:Left key path,要操作的集合對(duì)象王滤,若調(diào)用 valueForKeyPath: 方法的對(duì)象本來(lái)就是集合對(duì)象贺嫂,則可以省略;
  • collentionOperator:Collection operator雁乡,集合操作符第喳,一般以@開(kāi)頭;
  • keyPathToproperty:Right key path踱稍,要運(yùn)算的屬性曲饱。
    舉例
   Address *myAddress = [[Address alloc] init];
//
    [myself setValue:myAddress forKey:@"address"];

    //KeyPath為多級(jí)訪問(wèn)
    [myself setValue:@"shanghai" forKeyPath:@"address.city"];

    NSLog(@"-------city = %@",myself.address.city);

    NSLog(@"-------city = %@",[myself valueForKeyPath:@"address.city"]);
    NSMutableArray *array = [NSMutableArray array];

    for ( int i = 0 ; i < 5 ; i++ ) {

        Address *address = [[Address alloc] init];

        NSString *cityStr = [NSString stringWithFormat:@"city-%d",i];

        //批量賦值
        NSDictionary *dic = @{
                              @"city":cityStr,
                              @"street":@"street",
                              @"cityNumber":@(i),
                              @"streetNumber":@(100+i)
                              };

        [address setValuesForKeysWithDictionary:dic];

        [array addObject:address];
    }

    //返回?cái)?shù)組中保存的對(duì)象的屬性,返回值為數(shù)組
    NSArray *cityArray = [array valueForKeyPath:@"cityNumber"];
    NSLog(@"%@",cityArray);
    
    NSArray *streetNumberArray = [array valueForKeyPath:@"streetNumber"];
    //求平均值
    NSNumber *avg = [array valueForKeyPath:@"@avg.streetNumber"];
    NSLog(@"%@-%@",streetNumberArray,avg);

打印輸出:

-------city = shanghai
2021-01-18 16:28:30.373550+0800 005---Runtime應(yīng)用[73332:21674820] -------city = shanghai
2021-01-18 16:28:30.373673+0800 005---Runtime應(yīng)用[73332:21674820] (
    0,
    1,
    2,
    3,
    4
)
2021-01-18 16:28:30.373814+0800 005---Runtime應(yīng)用[73332:21674820] (
    100,
    101,
    102,
    103,
    104
)-102
集合運(yùn)算符的分類

集合運(yùn)算符主要分為三類:

  • 集合操作符:處理集合包含的對(duì)象珠月,并根據(jù)操作符的不同返回不同的類型扩淀,返回值以NSNumber為主。
  • 數(shù)組操作符:根據(jù)操作符的條件啤挎,將符合條件的對(duì)象包含在數(shù)組中返回驻谆。
  • 嵌套操作符:處理集合對(duì)象中嵌套其他集合對(duì)象的情況,返回結(jié)果也是一個(gè)集合對(duì)象侵浸。
1. 集合操作符

集合操作符處理 NSArray和 NSSet及其子類這樣的集合對(duì)象旺韭,并根據(jù)不同的操作符返回不同類型的對(duì)象,返回值一般都是NSNumber掏觉。

主要有以下幾種

  • @avg 用來(lái)計(jì)算集合中 right keyPath 指定的屬性的平均值区端。
    //@avg:平均值
    NSNumber *avg = [array valueForKeyPath:@"@avg.streetNumber"];
    NSLog(@"%@",avg);
  • @count用來(lái)計(jì)算集合中對(duì)象的數(shù)量。備注:@count 操作符比較特殊澳腹,它不需要寫 right keyPath织盼,即使寫了也會(huì)被忽略。
    //@count:集合里對(duì)象的數(shù)量
    NSNumber *count = [array valueForKeyPath:@"@count"];
    NSLog(@"%@",count);
  • @sum用來(lái)計(jì)算集合中 right keyPath 指定的屬性的總和酱塔。
    //@sum:總和
    NSNumber *sum = [array valueForKeyPath:@"@sum.streetNumber"];
    NSLog(@"%@",sum);
  • @max 用來(lái)查找集合中 right keyPath 指定的屬性的最大值沥邻。
    //@max:最大值
    NSNumber *max = [array valueForKeyPath:@"@max.cityNumber"];
    NSLog(@"%@",max);
  • @min 用來(lái)查找集合中 right keyPath 指定的屬性的最小值。
    //@min:最小值
    NSNumber *min = [array valueForKeyPath:@"@min.cityNumber"];
    NSLog(@"%@",min);

備注:@max 和 @min 在進(jìn)行判斷時(shí)羊娃,都是通過(guò)調(diào)用 compare:方法進(jìn)行判斷唐全,所以可以通過(guò)重寫該方法對(duì)判斷過(guò)程進(jìn)行控制。
加上一些打印

    NSArray *streetNumberArray = [array valueForKeyPath:@"streetNumber"];
    NSNumber *avg = [array valueForKeyPath:@"@avg.streetNumber"];
    NSNumber *count = [array valueForKeyPath:@"@count"];
    NSNumber *sum = [array valueForKeyPath:@"@sum.streetNumber"];
    NSNumber *max = [array valueForKeyPath:@"@max.cityNumber"];
    NSNumber *min = [array valueForKeyPath:@"@min.cityNumber"];

輸出:
2021-01-18 16:39:08.167698+0800 005---Runtime應(yīng)用[73377:21677966] (
    100,
    101,
    102,
    103,
    104
)-avg:102
 count:5
 sum:510
 max:4
 min:0
2. 數(shù)組操作符(因?yàn)榉祷氐氖菙?shù)組,所以叫數(shù)組操作符)
  • @unionOfObjects將集合中的所有對(duì)象的同一個(gè)屬性放在數(shù)組中返回邮利。這個(gè)和直接寫屬性好像沒(méi)區(qū)別弥雹。

NSArray *city = [array valueForKeyPath:@"@unionOfObjects.city"];
NSLog(@"%@",city);

NSArray *cityArray = [array valueForKeyPath:@"city"];
NSLog(@"%@",cityArray);

  • @distinctUnionOfObjects 將集合中對(duì)象的屬性進(jìn)行去重并返回。
    NSArray *streetNumberArr = [array valueForKeyPath:@"@distinctUnionOfObjects.streetNumber"];
    NSLog(@"%@",streetNumberArr);
    NSArray *city = [array valueForKeyPath:@"@unionOfObjects.city"];
    NSLog(@"%@",city);
    
    NSArray *street= [array valueForKeyPath:@"@distinctUnionOfObjects.street"];
       NSLog(@"%@",street);

打印結(jié)果:

2021-01-18 16:44:22.055668+0800 005---KVC[73410:21680170] (
    street
)
2021-01-18 16:44:22.055726+0800 005---KVC[73410:21680170] (
    "city-0",
    "city-1",
    "city-2",
    "city-3",
    "city-4"
)
3. 嵌套操作符

嵌套操作符是對(duì)集合里的集合進(jìn)行操作延届,比如數(shù)組里面的數(shù)組剪勿。

    NSMutableArray *array1 = [NSMutableArray array];

    for ( int i = 0 ; i < 5 ; i++ ) {

        Address *address = [[Address alloc] init];

        NSString *cityStr = [NSString stringWithFormat:@"city-%d",i];

        //批量賦值
        NSDictionary *dic = @{
                              @"city":cityStr,
                              @"street":@"street",
                              @"cityNumber":@(i),
                              @"streetNumber":@(100+i)
                              };

        [address setValuesForKeysWithDictionary:dic];

        [array1 addObject:address];
    }
    NSMutableArray *array2 = [NSMutableArray array];

    for ( int i = 3 ; i < 9 ; i++ ) {

        Address *address = [[Address alloc] init];

        NSString *cityStr = [NSString stringWithFormat:@"city-%d",i];

        //批量賦值
        NSDictionary *dic = @{
                              @"city":cityStr,
                              @"street":@"street",
                              @"cityNumber":@(i),
                              @"streetNumber":@(100+i)
                              };

        [address setValuesForKeysWithDictionary:dic];

        [array2 addObject:address];
    }

    NSArray *array = @[array1,array2];
    
    NSArray *cityArray = [array valueForKeyPath:@"city"];
    NSArray *result = [array valueForKeyPath:@"@unionOfArrays.city"];
    NSArray *disResult = [array valueForKeyPath:@"@distinctUnionOfArrays.city"];
    NSSet *resultSet = [array valueForKeyPath:@"@distinctUnionOfSets.city"];
    NSLog(@"%@",cityArray);
    NSLog(@"%@",result);
    NSLog(@"%@",disResult);
    NSLog(@"%@",resultSet);
  • @unionOfArrays是用來(lái)操作集合內(nèi)部的集合,將所有right keyPath對(duì)應(yīng)的屬性放在一個(gè)數(shù)組中返回方庭。
    NSArray *result = [array valueForKeyPath:@"@unionOfArrays.city"];

  • @distinctUnionOfArrays是用來(lái)操作集合內(nèi)部的集合對(duì)象厕吉,將所有right keyPath對(duì)應(yīng)的對(duì)象放在一個(gè)數(shù)組中,并進(jìn)行排重械念。
    NSArray *result = [array valueForKeyPath:@"@distinctUnionOfArrays.city"];

  • @distinctUnionOfSets 是用來(lái)操作集合內(nèi)部的集合對(duì)象头朱,將所有right keyPath對(duì)應(yīng)的對(duì)象放在一個(gè)set中,并進(jìn)行排重订讼。
    NSSet *result = [array valueForKeyPath:@"@distinctUnionOfSets.city"];

KVC性能

根據(jù)上面KVC的實(shí)現(xiàn)原理髓窜,我們可以看出KVC的性能并不如直接訪問(wèn)屬性快,雖然這個(gè)性能消耗是微乎其微的欺殿。所以在使用KVC的時(shí)候寄纵,建議最好不要手動(dòng)設(shè)置屬性的setter、getter脖苏,這樣會(huì)導(dǎo)致搜索步驟變長(zhǎng)程拭。

而且盡量不要用KVC進(jìn)行集合操作,例如NSArray棍潘、NSSet之類的恃鞋,集合操作的性能消耗更大,而且還會(huì)創(chuàng)建不必要的對(duì)象亦歉。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末恤浪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子肴楷,更是在濱河造成了極大的恐慌水由,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赛蔫,死亡現(xiàn)場(chǎng)離奇詭異砂客,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)呵恢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門鞠值,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人渗钉,你說(shuō)我怎么就攤上這事彤恶。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵声离,是天一觀的道長(zhǎng)歇竟。 經(jīng)常有香客問(wèn)我,道長(zhǎng)抵恋,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任宝磨,我火速辦了婚禮弧关,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唤锉。我一直安慰自己世囊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布窿祥。 她就那樣靜靜地躺著株憾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晒衩。 梳的紋絲不亂的頭發(fā)上嗤瞎,一...
    開(kāi)封第一講書(shū)人閱讀 51,208評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音听系,去河邊找鬼贝奇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛靠胜,可吹牛的內(nèi)容都是我干的掉瞳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼浪漠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼陕习!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起址愿,我...
    開(kāi)封第一講書(shū)人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤该镣,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后必盖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拌牲,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年歌粥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了塌忽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡失驶,死狀恐怖土居,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤擦耀,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布棉圈,位于F島的核電站,受9級(jí)特大地震影響眷蜓,放射性物質(zhì)發(fā)生泄漏分瘾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一吁系、第九天 我趴在偏房一處隱蔽的房頂上張望德召。 院中可真熱鬧,春花似錦汽纤、人聲如沸上岗。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)肴掷。三九已至,卻和暖如春背传,著一層夾襖步出監(jiān)牢的瞬間呆瞻,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工续室, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留栋烤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓挺狰,卻偏偏與公主長(zhǎng)得像明郭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丰泊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • KVC 是 Key-Value Coding的簡(jiǎn)稱薯定。是由NSKeyValueCoding非正式協(xié)議啟用的一種機(jī)制...
    Engandend閱讀 1,276評(píng)論 0 1
  • iOS 底層探索: 學(xué)習(xí)大綱 OC篇[/p/9d73ee7aae64] 前言 KVC用的好多,今天來(lái)看看他的底層是...
    歐德?tīng)栘己?/span>閱讀 461評(píng)論 0 3
  • 集合屬性操作 根據(jù)KVO的實(shí)現(xiàn)原理瞳购,是在運(yùn)行時(shí)生成新的子類并重寫其setter方法话侄,在其內(nèi)容發(fā)生改變時(shí)發(fā)送消息。但...
    _李恒閱讀 453評(píng)論 2 0
  • 該文章屬于劉小壯原創(chuàng)学赛,轉(zhuǎn)載請(qǐng)注明:劉小壯[http://www.reibang.com/u/2de707c93d...
    劉小壯閱讀 20,945評(píng)論 29 115
  • 推薦指數(shù): 6.0 書(shū)籍主旨關(guān)鍵詞:特權(quán)年堆、焦點(diǎn)、注意力盏浇、語(yǔ)言聯(lián)想变丧、情景聯(lián)想 觀點(diǎn): 1.統(tǒng)計(jì)學(xué)現(xiàn)在叫數(shù)據(jù)分析,社會(huì)...
    Jenaral閱讀 5,717評(píng)論 0 5