ios 底層 KVC原理

KVC 含義

咱們看下官方給的定義:ios

Key-value coding is a mechanism enabled by the NSKeyValueCoding informal protocol that objects adopt to provide indirect access to their properties. 【譯】KVC 是經過 NSKeyValueCoding 這個非正式協議啓用的一種機制,而遵循了這個協議的對象就提供了對其屬性的間接訪問。api

KVC 的用途

1.基本用法

直接對屬性或者成員變量進行取值和賦值數組

LGPerson *person = [[LGPerson alloc] init];
    [person setValue:@"KC" forKey:@"name"];
    [person setValue:@19 forKey:@"age"];
    [person setValue:@"酷C" forKey:@"myName"];
    NSLog(@"%@ - %@ - %@",[person valueForKey:@"name"],[person valueForKey:@"age"],[person valueForKey:@"myName"]);
複製代碼

2.操做集合類型

針對集合屬性,能夠直接經過mutableArrayValueForKey對對象的集合屬性進行操做更改bash

person.array = @[@"1",@"2",@"3"];
    // KVC 的方式
    NSMutableArray *ma = [person mutableArrayValueForKey:@"array"];
    ma[0] = @"100";
    NSLog(@"%@",[person valueForKey:@"array"]);

複製代碼

3.訪問非對象屬性

針對結構體,KVC也能夠直接操做,可是操做時候須要將結構體轉成NSValue類型dom

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"];
    NSValue *reslut = [person valueForKey:@"threeFloats"];
    NSLog(@"%@",reslut);
    
    ThreeFloats th;
    [reslut getValue:&th] ;
    NSLog(@"%f - %f - %f",th.x,th.y,th.z);
複製代碼

4.層層訪問

假如對象的屬性也是對象,那麼KVC能夠經過keyPath來操做對象屬性的屬性ide

LGStudent *student = [[LGStudent alloc] init];
    student.subject    = @"iOS";
    person.student     = student;
    [person setValue:@"大師班" forKeyPath:@"student.subject"];
    NSLog(@"%@",[person valueForKeyPath:@"student.subject"]);
複製代碼

5.集合操做符

5.1 字典操做

假如字典的key和一個對象的屬性都同樣,那麼能夠經過setValuesForKeysWithDictionary直接將字典的value賦值給對象相應的屬性,一樣,也能夠經過dictionaryWithValuesForKeys將對象轉換成字典ui

- (void)dictionaryTest{
    
    NSDictionary* dict = @{
                           @"name":@"Cooci",
                           @"nick":@"KC",
                           @"subject":@"iOS",
                           @"age":@18,
                           @"length":@180
                           };
    LGStudent *p = [[LGStudent alloc] init];
    // 字典轉模型
    [p setValuesForKeysWithDictionary:dict];
    NSLog(@"%@:%@",p,p.name);
    // 鍵數組轉模型到字典
    NSArray *array = @[@"name",@"age"];
    NSDictionary *dic = [p dictionaryWithValuesForKeys:array];
    NSLog(@"%@",dic);
}
複製代碼

5.2 操做數組元素的信息(KVC消息傳遞)

經過api能夠拿到數組元素的長度,也能夠對數組元素進行操做獲得新的數組spa

NSArray *array = @[@"Hank",@"Cooci",@"Kody",@"CC"];
    //獲得數組中全部元素的長度
    NSArray *lenStr= [array valueForKeyPath:@"length"];
    NSLog(@"%@",lenStr);
    //將數組中全部值全變成小寫
    NSArray *lowStr= [array valueForKeyPath:@"lowercaseString"];
    NSLog(@"%@",lowStr);
    //將數組中全部值全變成大寫
    NSArray *uppercaseStr= [array valueForKeyPath:@"uppercaseString"];
    NSLog(@"%@",uppercaseStr);
複製代碼

5.3 聚合操做符

@avg:取平均值 @count:取個數 @max:取最大值 @min:取最小值 @sum:求和指針

- (void)aggregationOperator{
    NSMutableArray *personArray = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        LGStudent *p = [LGStudent new];
        NSDictionary* dict = @{
                               @"name":@"Tom",
                               @"age":@(18+i),
                               @"nick":@"Cat",
                               @"length":@(175 + 2*arc4random_uniform(6)),
                               };
        [p setValuesForKeysWithDictionary:dict];
        [personArray addObject:p];
    }
    NSLog(@"%@", [personArray valueForKey:@"length"]);
    
    /// 平均身高
    float avg = [[personArray valueForKeyPath:@"@avg.length"] floatValue];
    NSLog(@"%f", avg);
    
    int count = [[personArray valueForKeyPath:@"@count.length"] intValue];
    NSLog(@"%d", count);
    
    int sum = [[personArray valueForKeyPath:@"@sum.length"] intValue];
    NSLog(@"%d", sum);
    
    int max = [[personArray valueForKeyPath:@"@max.length"] intValue];
    NSLog(@"%d", max);
    
    int min = [[personArray valueForKeyPath:@"@min.length"] intValue];
    NSLog(@"%d", min);
}
複製代碼

KVC 的底層實現

KVC-賦值過程

根據上面官方文檔得知:code

    1. 先依次查詢有沒有相關的方法:set< Key>:_set< Key>:setIs< Key>: 找到直接進行調用賦值。
    1. 若沒有相關方法時,會查看類方法accessInstanceVariablesDirectly是否爲YES時進入下一步。不然進入步驟4
    1. YES時,能夠直接訪問成員變量的來進行賦值,依次尋找變量 _< key >_is< Key>< key>is< Key>。找到則直接賦值,不然進入下一步。
  • 將會調用**setValue:forUndefinedKey:**方法進行拋出異常。能夠自定義的實現爲未找到的場景來避免拋出異常的行爲。

KVC-取值過程

    1. 先依次尋找是否有相關成員變量:get< Key>< key>is< Key>,或者 _< key >, 有則進入步驟4,不然下一步。
    1. 查看類方法accessInstanceVariablesDirectly是否爲YES,是則進入下一步,不然進入步驟5
    1. 依次尋找 _< key>_is< Key>< key>,或者 is< Key> 成員變量是否有值,有則進入步驟4,不然進入步驟5
    1. 若是檢索到的屬性值是對象指針,則只返回結果。若是值是支持的標量類型NSNumber,則將其存儲在NSNumber實例中並 返回該值。若是結果是NSNumber不支持的標量類型,則轉換爲NSValue對象並返回該對象。
    1. 將會調用 setValue:forUndefinedKey: 方法進行拋出異常。能夠自定義的實現爲未找到的場景來避免拋出異常的行爲

總結

在開發中常常用用到kvc,須要注意的是,ios13之後,對部分私有屬性,使用kvc的話,會崩潰,這點要注意。

相關文章
相關標籤/搜索