代码:数组
ViewController.mbash
person.name = @"LG_Cooci"; person.age = 18; person->myName = @"cooci"; NSLog(@"%@ - %d - %@",person.name,person.age,person->myName); 复制代码
打印结果:markdown
Janice - 18 - ty
复制代码
代码:atom
ViewController.mspa
[person setValue:@"Janice" forKey:@"name"]; [person setValue:@19 forKey:@"age"]; [person setValue:@"ty" forKey:@"myName"]; NSLog(@"%@ - %@ - %@",[person valueForKey:@"name"],[person valueForKey:@"age"],[person valueForKey:@"myName"]); 复制代码
打印结果:指针
Janice - 19 - ty
复制代码
代码:code
ViewController.morm
//不可变数组 person.array = @[@"1",@"2",@"3"]; NSArray *array = [person valueForKey:@"array"]; // 用 array 的值建立一个新的数组 array = @[@"100",@"2",@"3"]; [person setValue:array forKey:@"array"]; NSLog(@"%@",[person valueForKey:@"array"]); //可变数组 NSMutableArray *ma = [person mutableArrayValueForKey:@"array"]; ma[0] = @"200"; NSLog(@"%@",[person valueForKey:@"array"]); 复制代码
打印结果:对象
(
100,
2,
3
)
(
200,
2,
3
)
复制代码
代码:three
LGPerson.h
#import <Foundation/Foundation.h> #import "LGStudent.h" NS_ASSUME_NONNULL_BEGIN typedef struct { float x, y, z; } ThreeFloats; @interface LGPerson @property (nonatomic) ThreeFloats threeFloats; @end 复制代码
ViewController.m
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); 复制代码
打印结果:
{length = 12, bytes = 0x0000803f0000004000004040}
1.000000 - 2.000000 - 3.000000
复制代码
LGStudent *student = [[LGStudent alloc] init]; student.subject = @"语文"; person.student = student; [person setValue:@"数学" forKeyPath:@"student.subject"]; NSLog(@"%@",[person valueForKeyPath:@"student.subject"]); 复制代码
打印结果:
数学
复制代码
这个过程当中主要用成员变量来探索,为何要使用成员变量呢,由于属性在赋值的过程当中原本就会生成getter 和 setter 方法了,因此再用属性探索KVC赋值,会很差分辩。
根据官方文档可知 KVC setter 过程当中查找赋值的方法以下:
一、set<Key>: 或则 _set<key>
二、若是找不到第一步,查看 accessInstanceVariablesDirectly 返回是否为YES,若是返回YES,就继续查找一下方法:_<key>, _is<key>,<key>,或则 is<key>
依次来验证一下:
一、先找setter
代码:
LGPerson.h
#import <Foundation/Foundation.h> #import "LGStudent.h" NS_ASSUME_NONNULL_BEGIN @interface LGPerson : NSObject{ @public NSString *_name; NSString *_isName; NSString *name; NSString *isName; } @end 复制代码
LGPerson.m
#import "LGPerson.h" @implementation LGPerson #pragma mark - 关闭或开启实例变量赋值 + (BOOL)accessInstanceVariablesDirectly{ return YES; } //MARK: - setKey. 的流程分析 - (void)setName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } - (void)_setName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } - (void)setIsName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } @end 复制代码
ViewController.m
- (void)viewDidLoad { [super viewDidLoad]; LGPerson *person = [[LGPerson alloc] init]; // 1: KVC - 设置值的过程 [person setValue:@"Janice" forKey:@"name"]; NSLog(@"%@-%@-%@-%@",person->_name,person->_isName,person->name,person->isName); NSLog(@"%@-%@-%@",person->_isName,person->name,person->isName); NSLog(@"%@-%@",person->name,person->isName); NSLog(@"%@",person->isName); } 复制代码
运行结果:
二、 当 accessInstanceVariablesDirectly 为 YES
LGPerson.m 中代码稍微作一点改动,以下:
//MARK: - setKey. 的流程分析 //注释掉 //- (void)setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} - (void)_setName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } - (void)setIsName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } 复制代码
打印结果:
而后代码再作一点改动,以下
//MARK: - setKey. 的流程分析 //- (void)setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} //- (void)_setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} - (void)setIsName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } 复制代码
打印结果:
二、 当 accessInstanceVariablesDirectly 为 NO 时,作以下改动
#pragma mark - 关闭或开启实例变量赋值 + (BOOL)accessInstanceVariablesDirectly{ return NO; } //MARK: - setKey. 的流程分析 //- (void)setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} //- (void)_setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} //- (void)setIsName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} 复制代码
打印结果:
以上,打印估计有注意到了我下面打印的值所有都为 null,这是由于在重写方法时,并无复制的哈,如_name = name;
下面将重写方法全都注释掉,以下,再来打印成员变量的值,再将 accessInstanceVariablesDirectly 而后YES。
代码:
LGPerson.m
#pragma mark - 关闭或开启实例变量赋值 + (BOOL)accessInstanceVariablesDirectly{ return YES; } //MARK: - setKey. 的流程分析 //- (void)setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} //- (void)_setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} //- (void)setIsName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} 复制代码
ViewController.m
#import "ViewController.h" #import "LGPerson.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LGPerson *person = [[LGPerson alloc] init]; // 1: KVC - 设置值的过程 [person setValue:@"Janice" forKey:@"name"]; NSLog(@"%@-%@-%@-%@",person->_name,person->_isName,person->name,person->isName); NSLog(@"%@-%@-%@",person->_isName,person->name,person->isName); NSLog(@"%@-%@",person->name,person->isName); NSLog(@"%@",person->isName); } 复制代码
打印结果:
打印: LGPerson.h
#import <Foundation/Foundation.h> #import "LGStudent.h" NS_ASSUME_NONNULL_BEGIN @interface LGPerson : NSObject{ @public // NSString *_name; NSString *_isName; NSString *name; NSString *isName; } @end 复制代码
ViewController.m
#import "ViewController.h" #import "LGPerson.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LGPerson *person = [[LGPerson alloc] init]; // 1: KVC - 设置值的过程 [person setValue:@"Janice" forKey:@"name"]; // NSLog(@"%@-%@-%@-%@",person->_name,person->_isName,person->name,person->isName); NSLog(@"%@-%@-%@",person->_isName,person->name,person->isName); NSLog(@"%@-%@",person->name,person->isName); NSLog(@"%@",person->isName); } 复制代码
打印结果:
再验证最后一个
#import <Foundation/Foundation.h> #import "LGStudent.h" NS_ASSUME_NONNULL_BEGIN @interface LGPerson : NSObject{ @public // NSString *_name; // NSString *_isName; // NSString *name; NSString *isName; } @end 复制代码
ViewController.m
#import "ViewController.h" #import "LGPerson.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LGPerson *person = [[LGPerson alloc] init]; // 1: KVC - 设置值的过程 [person setValue:@"Janice" forKey:@"name"]; // NSLog(@"%@-%@-%@-%@",person->_name,person->_isName,person->name,person->isName); // NSLog(@"%@-%@-%@",person->_isName,person->name,person->isName); // NSLog(@"%@-%@",person->name,person->isName); // NSLog(@"%@",person->isName); } 复制代码
打印结果:
官方文档的查找流程以下,get<key>, <key>, is<key>,_<key>
代码:
LGPerson.h
@interface LGPerson : NSObject{
@public
NSString *_name;
NSString *_isName;
NSString *name;
NSString *isName;
}
@end
复制代码
LGPerson.m
- (NSString *)getName{ return NSStringFromSelector(_cmd); } - (NSString *)name{ return NSStringFromSelector(_cmd); } - (NSString *)isName{ return NSStringFromSelector(_cmd); } - (NSString *)_name{ return NSStringFromSelector(_cmd); } 复制代码
ViewController.m
- (void)viewDidLoad { [super viewDidLoad]; LGPerson *person = [[LGPerson alloc] init]; // 2: KVC - 取值的过程 //先赋值不一样的值 person->_name = @"_name"; person->_isName = @"_isName"; person->name = @"name"; person->isName = @"isName"; //取值 NSLog(@"取值:%@",[person valueForKey:@"name"]); } 复制代码
打印结果:
再验证一个,就将getName注释掉,走下一个方法
LGPerson.m
//MARK: - valueForKey 流程分析 - get<Key>, <key>, is<Key>, or _<key>, //- (NSString *)getName{ // return NSStringFromSelector(_cmd); //} - (NSString *)name{ return NSStringFromSelector(_cmd); } - (NSString *)isName{ return NSStringFromSelector(_cmd); } - (NSString *)_name{ return NSStringFromSelector(_cmd); } 复制代码
打印结果:
KVC设置与取值总结:
KVC 设置过程:
一、判断是否存在'set<key>' 或者 '_set<key>'(带下划线的属性)'setls<key>'
二、若是没有条件 1,(简单访问方式)
2.一、判断 'accessInstanceVariablesDirectly' 是否存在,返回 'YES'
2.二、判断 '_<key>','_is<key>','<key>','is<key>'等实例变量
2.三、直接给这些实例变量设置
三、'setValue:forUndefinedKey:'报错!
KVC 取值过程:
一、若是找到'get<key>',<key>,'is<key>','_<key>'这几个方法就跳到 '第五步'
二、若是没有条件 1 ,开始是不是 NSArray 判断
三、是不是 NSSet 判断
四、非集合类型
4.一、'accessInstanceVariablesDirectly' 返回 YES
4.二、'_<key>,_is<key>,,is<key>'这些实例属性
4.三、若是找到,直接获取实例变量的值,而后继续执行步骤 5
五、细节处理
5.一、若是检索到的属性值是对象指针,则只需返回结果
5.二、若是该值是 NSNumber 支持的标量类型,则将其存储在 NSNumber 实例中并返回它
5.三、若是结果是NSNumber 不支持的标量类型,则转换为 NSValue 对象并返回
六、'valueForUndefineKey'报错!!!
七、集合类型的还须要操做!
一、KVC 自动转换类型
如,
[person setValue:@"20" forKey:@"age"]; 复制代码
age 为 int 类型,可是给了一个 NSString 类型,因而,便会自动转换类型为 __NSCFNumber
二、能够对int,NSValue 类型设置空值,对NSString设空值变不会走设置的任何方法
当找不到时能够用一下方法能够检测到
- (void)setNilValueForKey:(NSString *)key; 复制代码
三、找不到Key
当找不到时能够用一下方法能够检测到
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 复制代码
四、取值时 - 找不到 key
当找不到时能够用一下方法能够检测到
- (nullable id)valueForUndefinedKey:(NSString *)key;
复制代码
五、键值验证
在此方法能够作一些容错处理
- (BOOL)validateValue:(inout id _Nullable * _Nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError; 复制代码