iOS底層探索20、KVC 原理

Key-Value Coding Programming Guide 蘋果文檔
KVC: Key-Value Coding

image.png

一、KVC 的簡單使用

1、普通賦值

1.1、setter -- llvm
    MyPerson *person = [[MyPerson alloc] init];
    person.name      = @"setName";
    person.age       = 18;
    person->myName   = @"yName";
    NSLog(@"%@ - %d - %@",person.name,person.age,person->myName);
// 輸出:setName - 18 - yName
1.2悄蕾、Key-Value Coding KVC
[person setValue:@"張三" forKey:@"name"];

2、集合類型

    person.array = @[@"1",@"2",@"3"];
    // 修改數(shù)組
    // person.array[0] = @"100";
    // 1: 建一個新的數(shù)組 - KVC 賦值
    NSArray *array = [person valueForKey:@"array"];
    array = @[@"100",@"2",@"3"];
    [person setValue:array forKey:@"array"];
    NSLog(@"%@",[person valueForKey:@"array"]);
    // 輸出:100 2 3
    // 2
    NSMutableArray *mArray = [person mutableArrayValueForKey:@"array"];
    mArray[0] = @"200";
    NSLog(@"%@",[person valueForKey:@"array"]);
    // 輸出:200 2 3

3础浮、訪問非對象屬性

以結(jié)構(gòu)體為例:

// 結(jié)構(gòu)體
typedef struct {
    float x, y, z;
} ThreeFloats;


    ThreeFloats floats = {1.,2.,3.};
    NSValue *value     = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
    [person setValue:value forKey:@"threeFloats"];// 設(shè)置
    NSValue *value1    = [person valueForKey:@"threeFloats"];// 取值
    NSLog(@"%@",value1);
    // 輸出:{length = 12, bytes = 0x0000803f0000004000004040}
    ThreeFloats th;
    [value1 getValue:&th];
    NSLog(@"%.2f-%.2f-%.2f",th.x,th.y,th.z);
    // 輸出:1.00-2.00-3.00

4帆调、keyPath

    MyStudent *student = [MyStudent alloc];
    student.subject    = @"subStr嗎";
    person.student     = student;
    [person setValue:@"subStr啊" forKeyPath:@"student.subject"];
    NSLog(@"%@",[person valueForKeyPath:@"student.subject"]);
    // 輸出:subStr啊

5奠骄、數(shù)組Array的操作

#pragma mark - array 取值
- (void)arrayDemo{
    MyStudent *p = [MyStudent new];
    p.penArr = [NSMutableArray arrayWithObjects:@"pen0", @"pen1", @"pen2", @"pen3", nil];
    NSArray *arr = [p valueForKey:@"penArr"]; // 動態(tài)成員變量
    NSLog(@"pens = %@", arr);
    /* pens = (
        pen0,
        pen1,
        pen2,
        pen3
    )*/
    //NSLog(@"%@",arr[0]);
    NSLog(@"%d",[arr containsObject:@"pen9"]);// 0 false
    // 遍歷
    NSEnumerator *enumerator = [arr objectEnumerator];
    NSString* str = nil;
    while (str = [enumerator nextObject]) {
        NSLog(@"%@", str);
    }
}

6、字典Dictionary

- (void)dictionaryDemo {
    
    // 1:
    NSDictionary *dict = @{
                           @"name":@"李四",
                           @"nick":@"小四",
                           @"subject":@"iOS",
                           @"age":@18,
                           @"length":@180
                           };
    MyStudent *p = [[MyStudent alloc] init];
    // 字典轉(zhuǎn)模型
    [p setValuesForKeysWithDictionary:dict];
    /**
      (lldb) p *$0
      (NSDictionary) $1 = {
        [0] = {
          key = 0x000000010585e3e0 @"age"
          value = 0x947567b2920d34b5 (int)18
        }
        [1] = {
          key = 0x000000010585e3a0 @"subject"
          value = 0x000000010585e3c0 @"iOS"
        }
        [2] = {
          key = 0x000000010585e360 @"nick"
          value = 0x000000010585e380 @"小四"
        }
        [3] = {
          key = 0x000000010585e0c0 @"name"
          value = 0x000000010585e340 @"李四"
        }
        [4] = {
          key = 0x000000010585e400 @"length"
          value = 0x947567b2920d3ed5 (int)180
        }
      }
    */


    // 2:
    // 數(shù)組 轉(zhuǎn) 模型 到 字典
    NSArray *array = @[@"name",@"age"];
    NSDictionary *dict2 = [p dictionaryWithValuesForKeys:array];
    NSLog(@"%@",dict2);
    /**
    (lldb) p *$2
    (NSDictionary) $3 = {
      [0] = {
        key = 0x000000010585e0c0 @"name"
        value = 0x000000010585e340 @"李四"
      }
      [1] = {
        key = 0x000000010585e3e0 @"age"
        value = 0x947567b2920d34b5 (int)18
      }
    }
    */
}

7番刊、KVC 消息傳遞

- (void)arrayMessagePass{
    NSArray *array = @[@"hank",@"anmyli",@"kod",@"CC"];
    NSArray *lenStr= [array valueForKeyPath:@"length"];
    NSLog(@"%@",lenStr);// 消息從 array 傳遞給了 string
    // string.length
//    (
//        4,
//        6,
//        3,
//        2
//    )
    NSArray *nStr= [array valueForKeyPath:@"uppercaseString"];// lowercaseString uppercaseString
    NSLog(@"%@",nStr);// 字母大寫
    // 輸出:
//    (
//        HANK,
//        ANMYLI,
//        KOD,
//        CC
//    )
}

8戚揭、NSSet

8.1)setKVC
- (void)setNesting{

//    NSSet *s = [NSSet setWithArray:@[@"1",@"3",@"8",@"4"]];
//    NSLog(@"%@",s);// 
    
    NSMutableSet *personSet1 = [NSMutableSet set];
    for (int i = 0; i < 6; i++) {
        MyStudent *person = [MyStudent new];
        NSDictionary* dict = @{
            @"name":@"Tom",
            @"age":@(18+i),
            @"nick":@"Cat",
            @"length":@(175 + 2*arc4random_uniform(6)),
        };
        [person setValuesForKeysWithDictionary:dict];
        [personSet1 addObject:person];
    }
    NSLog(@"personSet1 = %@", [personSet1 valueForKey:@"length"]);
//    輸出:
//    personSet1 = {(
//        175,
//        181,
//        177,
//        179
//    )}

    NSMutableSet *personSet2 = [NSMutableSet set];
    for (int i = 0; i < 6; i++) {
        MyPerson *person = [MyPerson new];
        NSDictionary* dict = @{
            @"name":@"jerry",
            @"age":@(18+i),
//            @"nick":@"Cat",
//            @"length":@(175 + 2*arc4random_uniform(6)),
        };
        [person setValuesForKeysWithDictionary:dict];
        [personSet2 addObject:person];
    }
    NSLog(@"personSet2 = %@", [personSet2 valueForKey:@"age"]);
//    輸出:
//    personSet2 = {(
//        21,
//        20,
//        23,
//        19,
//        22,
//        18
//    )}

    // 嵌套set
    NSSet* nestSet = [NSSet setWithObjects:personSet1, personSet2, nil];
    // 交集
    NSArray* arr1 = [nestSet valueForKeyPath:@"@distinctUnionOfSets.name"];
    NSLog(@"arr1 = %@", arr1);
//    輸出:
//    arr1 = {(
//        Tom,
//        jerry
//    )}
}
8.2)setarray 區(qū)別:

1、代碼調(diào)試
執(zhí)行如下代碼:

    NSMutableSet *personSet1 = [NSMutableSet set];
    NSMutableArray *personArr1 = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        MyStudent *person = [MyStudent new];
        NSDictionary* dict = @{
            @"name":@"Tom",
            @"age":@(18+i),
            @"nick":@"Cat",
            @"length":@(175 + 2*arc4random_uniform(6)),
        };
        [person setValuesForKeysWithDictionary:dict];
        [personSet1 addObject:person];
        [personArr1 addObject:person];
    }

打個斷點在 for 所在行撵枢,調(diào)試信息如下:

(lldb) p malloc_size((__bridge void *)personArr1)
(size_t) $0 = 48
(lldb) p malloc_size((__bridge void *)personSet1)
(size_t) $1 = 32
(lldb) 

繼續(xù)執(zhí)行,并調(diào)試:

(lldb) po personArr1
<__NSArrayM 0x600003e33f60>(
<MyStudent: 0x600003e307e0>,
<MyStudent: 0x600003e33f90>,
<MyStudent: 0x600003e33fc0>,
<MyStudent: 0x600003e30750>,
<MyStudent: 0x600003e324f0>,
<MyStudent: 0x600003e308a0>
)

(lldb) po personSet1
{(
    <MyStudent: 0x600003e33fc0>,
    <MyStudent: 0x600003e33f90>,
    <MyStudent: 0x600003e30750>,
    <MyStudent: 0x600003e324f0>,
    <MyStudent: 0x600003e307e0>,
    <MyStudent: 0x600003e308a0>
)}

8.2)官方文檔 OC 下 - Values and Collections

NSSet - NSSet :

image.png

NSArray:

image.png

這里把字典的圖示也放在這里便于比較精居,Dictionary:

image.png

setarray主要區(qū)別:
array有序锄禽,數(shù)據(jù)可重復(fù);
set無序靴姿,數(shù)據(jù)不重復(fù)沃但。

二、KVC 原理探究 基于蘋果官方文檔

Accessor Search Patterns 文檔地址

1佛吓、Search Pattern for the Basic Setter

image.png

setter 的流程:

  1. 先找set<Key>: or _set<Key>宵晚;
  2. 若沒找到且accessInstanceVariablesDirectly returns YES,則繼續(xù)找_<key>, _is<Key>, <key>, or is<Key>;
  3. 沒找著則: invoke setValue:forUndefinedKey:.维雇。默認情況下會引發(fā)一個異常淤刃,但NSObject的子類可能提供特定于鍵的行為

2、Search Pattern for the Basic Getter

image.png

getter流程:

  1. 實例中搜索吱型,先找 get<Key>, <key>, is<Key>, or _<key>逸贾,找到了就調(diào)用去·5·中處理結(jié)果;沒找著則繼續(xù)下一步津滞;
  2. 搜索名稱與模式 countOf<Key>objectIn<Key>AtIndex: (對應(yīng)于NSArray類定義的基本方法) 以及 <key>AtIndexes: (對應(yīng)于NSArray 方法 objectsAtIndexes:)铝侵;
    2.1.、如果找到第1個或至少兩個中的一個触徐,則創(chuàng)建一個集合代理對象咪鲜,該對象響應(yīng)所有NSArray方法并返回該方法。否則撞鹉,繼續(xù)執(zhí)行步驟3疟丙;
    2.2、代理對象隨后將接收到的任何NSArray消息轉(zhuǎn)換為countOf<Key>鸟雏、objectIn<Key>AtIndex:<Key> AtIndexes:的一些組合隆敢,這些組合將消息發(fā)送給創(chuàng)建它的符合鍵值編碼的對象。如果原始對象還實現(xiàn)了一個名為get<Key>:range:的可選方法崔慧,代理對象也會在適當(dāng)?shù)臅r候使用它拂蝎。實際上,代理對象與鍵值編碼兼容的對象一起工作惶室,允許底層屬性像NSArray一樣工作温自,即使它不是NSArray玄货。
  3. 如果沒有找到簡單的訪問方法或數(shù)組訪問方法組,查找下面三個方法悼泌,countOf<Key>松捉,enumeratorOf<Key>,memberOf<Key>:(對應(yīng)于NSSet類定義的原語方法)。
    3.1馆里、如果這三個方法都找到了隘世,創(chuàng)建一個集合代理對象,它響應(yīng)所有NSSet方法并返回那個鸠踪。否則丙者,繼續(xù)執(zhí)行步驟4。
    3.2营密、這個代理對象隨后將它接收到的任何NSSet消息轉(zhuǎn)換為countOf<Key>械媒、enumeratorOf<Key>memberOf<Key>:消息的組合,并發(fā)送給創(chuàng)建它的對象评汰。實際上纷捞,代理對象與遵循鍵值編碼的對象一起工作,允許底層屬性像NSSet一樣運行被去,即使它不是NSSet主儡。
  4. 如果沒有找到簡單的訪問方法或集合訪問方法組,并且如果接收方的類方法accessinstancevariables返回YES惨缆,那么按照順序搜索一個實例變量:_<key>缀辩, _is< key><key>踪央,或is< key>臀玄。如果找到,直接獲取實例變量的值并繼續(xù)執(zhí)行步驟5畅蹂。否則健无,繼續(xù)執(zhí)行步驟6。
  5. 如果檢索到的屬性值是一個對象指針液斜,只需返回結(jié)果累贤。
    如果值是NSNumber支持的標(biāo)量類型,將其存儲在NSNumber實例中并返回少漆。
    如果結(jié)果是NSNumber不支持的標(biāo)量類型臼膏,轉(zhuǎn)換為NSValue對象并返回它。
  6. 如果所有其他方法都失敗示损,則調(diào)用valueForUndefinedKey:渗磅。這在默認情況下會引發(fā)一個異常,但是NSObject的一個子類可能提供特定于鍵的行為。

3始鱼、Getting/Setting Attribute Values Using Keys

對應(yīng)的 Apple 地址

image.png

image.png

更多信息在蘋果文檔中比較詳細仔掸,這里不再多做介紹。

三医清、自定義 KVC

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末起暮,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子会烙,更是在濱河造成了極大的恐慌负懦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柏腻,死亡現(xiàn)場離奇詭異纸厉,居然都是意外死亡,警方通過查閱死者的電腦和手機葫盼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來村斟,“玉大人贫导,你說我怎么就攤上這事◇№铮” “怎么了孩灯?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長逾滥。 經(jīng)常有香客問我峰档,道長,這世上最難降的妖魔是什么寨昙? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任讥巡,我火速辦了婚禮,結(jié)果婚禮上舔哪,老公的妹妹穿的比我還像新娘欢顷。我一直安慰自己,他們只是感情好捉蚤,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布抬驴。 她就那樣靜靜地躺著,像睡著了一般缆巧。 火紅的嫁衣襯著肌膚如雪布持。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天陕悬,我揣著相機與錄音题暖,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛芙委,可吹牛的內(nèi)容都是我干的逞敷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼灌侣,長吁一口氣:“原來是場噩夢啊……” “哼推捐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起侧啼,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤牛柒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后痊乾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體皮壁,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年哪审,在試婚紗的時候發(fā)現(xiàn)自己被綠了蛾魄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡湿滓,死狀恐怖滴须,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叽奥,我是刑警寧澤扔水,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站朝氓,受9級特大地震影響魔市,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赵哲,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一待德、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧枫夺,春花似錦磅网、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至毙死,卻和暖如春燎潮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背扼倘。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工确封, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留除呵,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓爪喘,卻偏偏與公主長得像颜曾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子秉剑,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353