底层

iOS-OC底层17:KVC原理

2020-10-26  本文已影响0人  MonKey_Money

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: 方法,抛出异常。
上一篇下一篇

猜你喜欢

热点阅读