iOS-OC底层17:KVC原理

KVC 简介

KVC全称是Key Value Coding(键值编码),是一个基于NSKeyValueCoding非正式协议实现的机制,它可以直接通过key值对对象的属性进行存取操作,而不需通过调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性,而不是在编译时确定。
KVC提供了一种间接访问属性方法或成员变量的机制,可以通过字符串来访问对象的的属性方法或成员变量。
在实现了访问器方法的类中,使用点语法和KVC访问对象其实差别不大,二者可以任意混用(因为KVC会首先搜索访问器方法,)。但是没有访问器方法的类中,点语法无法使用,这时KVC就有优势了。
KVC和KVO都是基于OC的动态特性和Runtime机制的。

基本使用

创建一个类

@interface MyPerson : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *hobby;
@property (nonatomic, copy) NSArray *array;

@end

通过keyvaluecode设值 取值

  MyPerson *person = [[MyPerson alloc] init];
    [person setValue:@"cooci" forKey:@"name"];
    [person setValue:@"篮球" forKey:@"hobby"];
    NSLog(@"%@的爱好是%@",[person valueForKey:@"name"],[person valueForKey:@"hobby"]);
    NSDictionary *dictionary = @{
        @"name":@"hank",
        @"hobby":@"足球",
    };
    MyPerson *person2 = [[MyPerson alloc] init];
    [person2 setValuesForKeysWithDictionary:dictionary];
    NSLog(@"%@的爱好是%@",person2.name,person2.hobby);
 NSArray *keyArray = @[@"name",@"hobby"];
    NSDictionary *valueDictionary = [person2 dictionaryWithValuesForKeys:keyArray];
    NSLog(@"value 值%@",valueDictionary);

打印结果

cooci的爱好是篮球
hank的爱好是足球
value 值{
    hobby = "\U8db3\U7403";
    name = hank;
}

修改数组

    MyPerson *person = [[MyPerson alloc] init];
    person.array = @[@"1",@"2",@"3"];
//取值
    NSLog(@"%@",[person valueForKey:@"array"]);
 //直接通过setValue forKey修改
    NSArray *array1 = @[@"2",@"2",@"3"];
    [person setValue:array1 forKey:@"array"];
    NSLog(@"%@",person.array);
//取出数组修改
    NSMutableArray *mutableArray = [person mutableArrayValueForKey:@"array"];
    mutableArray[0] = @"200";
    NSLog(@"%@",person.array);

打印结果

(
    1,
    2,
    3
)
 (
    2,
    2,
    3
)
 (
    200,
    2,
    3
)

集合操作符

NSArray *array = @[@"Hank",@"Cooci",@"Kody",@"CC"];
//获取数组内字符串的长度
   NSArray *lenStr= [array valueForKeyPath:@"length"];
    NSLog(@"%@",lenStr);
//让数组内的字符串小写
NSArray *lowStr= [array valueForKeyPath:@"lowercaseString"];
    NSLog(@"%@",lowStr);

打印结果

 (
    4,
    5,
    4,
    2
)
(
    hank,
    cooci,
    kody,
    cc
)

对数组中对象的属性聚合操作

- (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);
}

打印结果

 (
    183,
    181,
    175,
    181,
    185,
    179
)
2020-10-26 15:49:50.477336+0800 001-KVC简介[92636:366961] 180.666672
2020-10-26 15:49:50.478062+0800 001-KVC简介[92636:366961] 6
2020-10-26 15:49:50.478566+0800 001-KVC简介[92636:366961] 1084
2020-10-26 15:49:50.479016+0800 001-KVC简介[92636:366961] 185
2020-10-26 15:49:50.480592+0800 001-KVC简介[92636:366961] 175

数组操作符

- (void)arrayOperator{
    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"]);
    // 返回操作对象指定属性的集合
    NSArray* arr1 = [personArray valueForKeyPath:@"@unionOfObjects.length"];
    NSLog(@"arr1 = %@", arr1);
    // 返回操作对象指定属性的集合 -- 去重
    NSArray* arr2 = [personArray valueForKeyPath:@"@distinctUnionOfObjects.length"];
    NSLog(@"arr2 = %@", arr2);
    
}

打印

(
    175,
    185,
    183,
    175,
    181,
    185
)
 arr1 = (
    175,
    185,
    183,
    175,
    181,
    185
)
 arr2 = (
    175,
    185,
    181,
    183
)

数组中嵌套数组

- (void)arrayNesting{
    
    NSMutableArray *personArray1 = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        LGStudent *student = [LGStudent new];
        NSDictionary* dict = @{
                               @"name":@"Tom",
                               @"age":@(18+i),
                               @"nick":@"Cat",
                               @"length":@(175 + 2*arc4random_uniform(6)),
                               };
        [student setValuesForKeysWithDictionary:dict];
        [personArray1 addObject:student];
    }
    
    NSMutableArray *personArray2 = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        LGStudent *person = [LGStudent new];
        NSDictionary* dict = @{
                               @"name":@"Tom",
                               @"age":@(18+i),
                               @"nick":@"Cat",
                               @"length":@(175 + 2*arc4random_uniform(6)),
                               };
        [person setValuesForKeysWithDictionary:dict];
        [personArray2 addObject:person];
    }
    
    // 嵌套数组
    NSArray* nestArr = @[personArray1, personArray2];
    
    NSArray* arr = [nestArr valueForKeyPath:@"@distinctUnionOfArrays.length"];
    NSLog(@"arr = %@", arr);
    
    NSArray* arr1 = [nestArr valueForKeyPath:@"@unionOfArrays.length"];
    NSLog(@"arr1 = %@", arr1);
}

打印结果

arr = (
    185,
    181,
    179,
    175
)
arr1 = (
    185,
    175,
    185,
    175,
    181,
    175,
    175,
    179,
    179,
    181,
    179,
    175
)

集合中嵌套集合

- (void)setNesting{
    
    NSMutableSet *personSet1 = [NSMutableSet set];
    for (int i = 0; i < 6; i++) {
        LGStudent *person = [LGStudent 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"]);
    
    NSMutableSet *personSet2 = [NSMutableSet set];
    for (int i = 0; i < 6; i++) {
        LGStudent *person = [LGStudent new];
        NSDictionary* dict = @{
                               @"name":@"Tom",
                               @"age":@(18+i),
                               @"nick":@"Cat",
                               @"length":@(175 + 2*arc4random_uniform(6)),
                               };
        [person setValuesForKeysWithDictionary:dict];
        [personSet2 addObject:person];
    }
    NSLog(@"personSet2 = %@", [personSet2 valueForKey:@"length"]);

    // 嵌套set
    NSSet* nestSet = [NSSet setWithObjects:personSet1, personSet2, nil];
    // 交集
    NSArray* arr1 = [nestSet valueForKeyPath:@"@distinctUnionOfSets.length"];
    NSLog(@"arr1 = %@", arr1);
}

打印结果

personSet1 = {(
    181,
    177,
    183,
    179
)}
personSet2 = {(
    181,
    177,
    183,
    179
)}
arr1 = {(
    181,
    177,
    183,
    179
)}

KVC原理

kvc的设值和取值流程可以参看

setter设值顺序

1.查找 set<Key>: 或 _set<Key>: 命名的 setter,按照这个顺序,如果找到,则调用这个方法并将值传进去。
2.如果没有发现一个简单的 setter ,但是 accessInstanceVariablesDirectly 类属性返回YES,则查找一个命名规则为 _key、_isKey、key、isKey的实例变量。按照这个顺序,如果查找到则将value赋值给实例变量。
3.如果没有找到 setter 或实例变量,则调用 setValue:forUndefinedKey: 方法,并默认抛出一个异常。

getter取值流程

  1. 首先按 get<Key>、<key>、is<Key> 的顺序查找 getter 方法,找到直接调用。
    • 若方法的返回结果类型是一个对象指针,则直接返回结果。
    • 若类型为能够转化为 NSNumber 的基本数据类型,转换为 NSNumber 后返回;否则转换为 NSValue 返回。
  2. 若上面的 getter没有找到,则查找 countOf<Key>、objectIn<Key>AtIndex:、<Key>AtIndexes 格式的方法。如果 countOf<Key> 和另外两个方法中的一个找到,那么就会返回一个可以响应 NSArray 所有方法的集合代理。发送给这个代理集合的 NSArray 消息方法,就会以 countOf<Key>、objectIn<Key>AtIndex:、<Key>AtIndexes 这几个方法组合的形式调用。如果 receiver 的类实现了 get<Key>:range: 方法,该方法也会用于性能优化。
  3. 还没查到,那么查找 countOf<Key>、enumeratorOf<Key>、memberOf<Key>: 格式的方法。如果这3个方法都找到,那么久返回一个可以相应NSSet所有方法的集合代理。发送给这个代理集合的NSSet消息方法,就会以countOf<Key>、enumeratorOf<Key>、memberOf<Key>: 组合的形式调用。
  4. 还是没查到,那么如果类方法 accessInstanceVariablesDirectly返回YES,那么按_<key>、_is<Key>、<key>、is<Key> 的顺序直接搜索实例变量。如果搜索到了,则返回receiver相应实例变量的值。
  5. 再没有查到,调用 valueForUndefinedKey: 方法,抛出异常。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352