iOS底层原理之kvc原理分析

1.kvc:Key-Value Coding

  • 基本类型使用
- (void)setValue:(nullable id)value forKey:(NSString *)key;
- (nullable id)valueForKey:(NSString *)key;
  • 集合类型使用
@property (nonatomic, strong) NSArray           *array;

// 修改不可变数组array的第一个值,
person.array = @[@"1",@"2",@"3"];

// 方法一,修改为888
NSArray *array = @[@"888",@"2",@"3"];
 [person setValue:array forKey:@"array"];
NSLog(@"方法1:array = %@",[person valueForKey:@"array"]);

// 放方二,kvc的方法,修改为999
NSMutableArray *ma = [person mutableArrayValueForKey:@"array"];
ma[0] = @"999";
NSLog(@"方法2:array = %@",[person valueForKey:@"array"]);
// 打印值
2020-02-13 19:30:11.594007+0800 001-KVC简介[51813:1163702] 方法1:array = (
    888,
    2,
    3
)
2020-02-13 19:30:11.594277+0800 001-KVC简介[51813:1163702] 方法2:array = (
    999,
    2,
    3
)
  • 非对象类型,转换成相应的NSValue,取值的时候,使用getValue接收取到的reslutValue
    ThreeFloats floats = {1., 2., 3.};
    NSValue *value  = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
    [person setValue:value forKey:@"threeFloats"];
    NSValue *reslutValue = [person valueForKey:@"threeFloats"];
    NSLog(@"value = %@",reslutValue);

    // 创建一个同类型结构体用来接收reslutValue
    ThreeFloats th;
    [reslutValue getValue:&th] ;
    NSLog(@"%f - %f - %f",th.x,th.y,th.z);
// 打印值
2020-02-13 19:44:07.411199+0800 001-KVC简介[51917:1170877] value = {length = 12, bytes = 0x0000803f0000004000004040}
2020-02-13 19:44:07.411318+0800 001-KVC简介[51917:1170877] 1.000000 - 2.000000 - 3.000000

2. kvc的赋值过程 Setter

  • 官方文档
iOS底层原理之kvc原理分析_第1张图片
image.png
  • 1.进行setter方法查找,

  • 2.进行成员变量查找,

  • 3.未找到则调用setValue:forUndefinedKey

  • 准备测试类验证

@interface KVCObject : NSObject
{
    @public
     NSString *_name;
     NSString *_isName;
     NSString *name;
     NSString *isName;
}
@end

KVCObject *ko = [KVCObject new];
// 1: KVC - 赋值过程
[ko setValue:@"kvc_name" forKey:@"name"];
2.1 set or _set 调用
  • 会依次调用setKey_setKeysetIsKey这三种方法

  • 不会调用_setIsKey,此方法不被调用

  • setter方法顺序验证

- (void)setName:(NSString *)name{
    NSLog(@"%s - %@",__func__,name);
}

- (void)_setName:(NSString *)name{
    NSLog(@"%s - %@",__func__,name);
}

- (void)setIsName:(NSString *)name{
    NSLog(@"%s - %@",__func__,name);
}
  • 依次访问顺序
KVC取值&赋值过程[52000:1175468] -[KVCObject setName:] - kvc_name
KVC取值&赋值过程[52021:1177222] -[KVCObject _setName:] - kvc_name
KVC取值&赋值过程[52033:1177853] -[KVCObject setIsName:] - kvc_name
2.2 成员变量赋值顺序
  • 当2.1寻找未果,并且accessInstanceVariablesDirectly返回YES的时候,会进行成员变量查找,如果返回NO,则跳过该步骤,直接到最后一步setValue: forUndefinedKey:

  • 验证代码

    NSLog(@"_name = %@",ko->_name);
    NSLog(@"_isName = %@",ko->_isName);
    NSLog(@"name = %@",ko->name);
    NSLog(@"isName = %@",ko->isName);
2.2.1 当accessInstanceVariablesDirectly返回NO
+ (BOOL)accessInstanceVariablesDirectly {
    return NO;
}
  • 结果如下,所有成员变量均未被赋值
2020-02-13 20:49:11.858754+0800 002-KVC取值&赋值过程[52465:1208541] name is Undefined
2020-02-13 20:49:11.858923+0800 002-KVC取值&赋值过程[52465:1208541] _name = (null)
2020-02-13 20:49:11.859015+0800 002-KVC取值&赋值过程[52465:1208541] _isName = (null)
2020-02-13 20:49:11.859231+0800 002-KVC取值&赋值过程[52465:1208541] name = (null)
2020-02-13 20:49:11.859378+0800 002-KVC取值&赋值过程[52465:1208541] isName = (null)
2.2.2 当accessInstanceVariablesDirectly返回YES
  • 开始查找成员变量
  • 顺序: _, _is, , or is
// 1. 四个成员变量均在_name,_isName,name,isName
2020-02-13 20:12:32.219946+0800 002-KVC取值&赋值过程[52173:1188008] _name = kvc_name
2020-02-13 20:12:32.220073+0800 002-KVC取值&赋值过程[52173:1188008] _isName = (null)
2020-02-13 20:12:32.220160+0800 002-KVC取值&赋值过程[52173:1188008] name = (null)
2020-02-13 20:12:32.220237+0800 002-KVC取值&赋值过程[52173:1188008] isName (null)

// 2. 三个成员变量均在 _isName, name, isName
2020-02-13 20:13:06.580442+0800 002-KVC取值&赋值过程[52191:1188733] _isName = kvc_name
2020-02-13 20:13:06.580567+0800 002-KVC取值&赋值过程[52191:1188733] name = (null)
2020-02-13 20:13:06.580659+0800 002-KVC取值&赋值过程[52191:1188733] isName (null)

// 3. 两个成员变量均在 name, isName
2020-02-13 20:20:08.235096+0800 002-KVC取值&赋值过程[52255:1193023] name = kvc_name
2020-02-13 20:20:08.235226+0800 002-KVC取值&赋值过程[52255:1193023] isName = (null)

// 4. 只剩一个成员变量均在 isName
2020-02-13 20:20:46.698804+0800 002-KVC取值&赋值过程[52271:1193754] isName = kvc_name
2.3 未找到所需要的key时
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    NSLog(@"%@ is Undefined", key);
}

// 打印
2020-02-13 20:46:31.007423+0800 002-KVC取值&赋值过程[52421:1206111] name is Undefined

3. kvc的取值过程 Getter(下面为非集合类型取值过程)

  • 1.getter方法取值,如果去到值直接返回
  • 2.accessInstanceVariablesDirectly返回YES,然后访问成员变量
  • 3.valueForUndefinedKey
    ko->_name = @"_name";
    ko->_isName = @"_isName";
    ko->name = @"name";
    ko->isName = @"isName";
    
    NSLog(@"取值:name = %@",[person valueForKey:@"name"]);
3.1 getter方法查找
  • 调用顺序: get, , is, or _
  • 验证
// getter方法
- (NSString *)getName{
    return @"getter: getName";
}

- (NSString *)name{
    return @"getter: name";
}

- (NSString *)isName{
    return @"getter: isName";
}

- (NSString *)_name{
    return @"getter: _name";
}
  • 结果
// 1
2020-02-13 21:02:29.167715+0800 002-KVC取值&赋值过程[52536:1214478] 取值:name = getter: getName

// 2
2020-02-13 21:04:11.983149+0800 002-KVC取值&赋值过程[52556:1215778] 取值:name = getter: name

// 3
2020-02-13 21:04:35.398485+0800 002-KVC取值&赋值过程[52571:1216422] 取值:name = getter: isName

// 4
2020-02-13 21:04:50.205883+0800 002-KVC取值&赋值过程[52581:1216985] 取值:name = getter: _name
3.2 成员变量获取
3.2.1 accessInstanceVariablesDirectly返回YES
  • 注意:accessInstanceVariablesDirectly返回NO,直接到valueForUndefinedKey
  • 访问成员变量获取顺序: _, _is, , or is
// 1
    ko->_name = @"_name";
    ko->_isName = @"_isName";
    ko->name = @"name";
    ko->isName = @"isName";
2020-02-13 21:09:31.594596+0800 002-KVC取值&赋值过程[52624:1220018] 取值:name = _name

// 2
    ko->_isName = @"_isName";
    ko->name = @"name";
    ko->isName = @"isName";
2020-02-13 21:22:54.308941+0800 002-KVC取值&赋值过程[52770:1229311] 取值:name = _isName

// 3
    ko->name = @"name";
    ko->isName = @"isName";
2020-02-13 21:23:32.312669+0800 002-KVC取值&赋值过程[52790:1230070] 取值:name = name

// 4
    ko->isName = @"isName";
2020-02-13 21:24:02.391724+0800 002-KVC取值&赋值过程[52809:1230787] 取值:name = isName

3.2.2 valueForUndefinedKey
- (id)valueForUndefinedKey:(NSString *)key {
    return [NSString stringWithFormat:@"%@-is-Undefined", key];
}
// 结果
2020-02-13 21:19:29.520438+0800 002-KVC取值&赋值过程[52698:1226144] 取值:name = name-is-Undefined

4 kvc的取值过程 之集合类型

  • 集合类型的取值需要多实现一些方法
  • countOf,objectInAtIndex:和AtIndexes:
@property (nonatomic, strong) NSArray *arr;

// 增加的方法
- (NSInteger)countOfPens {
    NSLog(@"%s",__func__);
    return self.arr.count;
}
- (id)objectInPensAtIndex:(NSUInteger)index {
    NSLog(@"%s",__func__);
    return [NSString stringWithFormat:@"pens %lu", index];
}
  • 试验一下
    ko.arr = @[@"pen0", @"pen1"];
    NSArray *array = [ko valueForKey:@"pens"];
    NSLog(@"%d", array.count);
    NSLog(@"%@",[array objectAtIndex:1]);

// 打印
2020-02-13 21:45:00.231940+0800 002-KVC取值&赋值过程[53098:1247507] -[KVCObject countOfPens]
2020-02-13 21:45:00.232067+0800 002-KVC取值&赋值过程[53098:1247507] 2
2020-02-13 21:45:00.232178+0800 002-KVC取值&赋值过程[53098:1247507] -[KVCObject objectInPensAtIndex:]
2020-02-13 21:45:00.232267+0800 002-KVC取值&赋值过程[53098:1247507] pens 1

5 总结

  • kvc赋值
  1. setter方法调用,setKey_setKeysetIsKey
  2. accessInstanceVariablesDirectly返回YES时进行成员变量赋值:_, _is, , or is
  3. setValue: forUndefinedKey:
  • kvc取值,非集合类型
  1. getter方法查找:get, , is, or _
  2. accessInstanceVariablesDirectly返回YES时进行成员变量取值:_, _is, , or is
  3. valueForUndefinedKey
  • kvc取值,集合类型

需要多实现一些方法,类似countOf,objectInAtIndex:和AtIndexes:类似这些

  • 基本原理差不多就这些了,欢迎大家一起讨论! 诙谐学习,不干不燥~

你可能感兴趣的:(iOS底层原理之kvc原理分析)