iOS KVC(一)基本了解
iOS KVC (二) 不可不知的赋值深层次原理
iOS KVC (三)不可不知的取值深层次原理
iOS KVC (四)keyPath的深度解析
iOS KVC (五)KVC几种典型的异常处理
iOS KVC (六) KVC容器类及深层次原理
iOS KVC(七) KVC正确性的验证
iOS KVC (八) KVC几种常见应用
iOS KVC (九) KVC模型转化(1) 模型打印 description, debugDescription
iOS KVC (十)模型转换(2)模型转换
KVC赋值
赋值的过程看似很简单,就是找到对应的key,然后将一个已经准备好的值赋过去,但是你知道深层次的原理吗?方法- (nullable id)valueForKey:(NSString *)key;中的key系统内部和底层是按照什么逻辑找的呢?下面就让我们看一下。
下面就是寻找key的内部流程。以[self setValue:@"小明" forKey:@"name"];这句代码作为例子进行说明。
程序优先调用set<Key>:属性值方法,代码通过setter方法完成设置。注意,这里的<key>是指成员变量名,首字母大小写要符合KVC的命名规则,下同。
如果没有找到setName:方法,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly方法有没有返回YES,默认该方法会返回YES,如果你重写了该方法让其返回NO的话,那么在这一步KVC会执行setValue:forUndefinedKey:方法,不过一般开发者不会这么做。所以KVC机制会搜索该类里面有没有名为<key>的成员变量,无论该变量是在类接口处定义,还是在类实现处定义,也无论用了什么样的访问修饰符,只在存在以<key>命名的变量,KVC都可以对该成员变量赋值。
如果该类即没有set<key>:方法,也没有_<key>成员变量,KVC机制会搜索_is<Key>的成员变量。
和上面一样,如果该类即没有set<Key>:方法,也没有_<key>和_is<Key>成员变量,KVC机制再会继续搜索<key>和is<Key>的成员变量。再给它们赋值。
如果上面列出的方法或者成员变量都不存在,系统将会执行该对象的setValue:forUndefinedKey:方法,默认是抛出异常。
特别需要注意的是:如果开发者想让这个类禁用KVC里,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set<Key>:属性名时,会直接用setValue:forUndefinedKey:方法。
下面结合例子进行说明
+ (BOOL)accessInstanceVariablesDirectly 返回NO实例
实例
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
{
NSString *name;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self setValue:@"小明" forKey:@"name"];
NSLog(@"name = %@", name);
}
+ (BOOL)accessInstanceVariablesDirectly
{
return NO;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
NSLog(@"没有找到该key对应的属性会抛出异常");
}
打印数据:
2018-05-16 14:45:42.959735+0700 KVC[21922:462925] 没有找到该key对应的属性会抛出异常
2018-05-16 14:45:42.959850+0700 KVC[21922:462925] name = (null)
总结:这里+ (BOOL)accessInstanceVariablesDirectly返回了NO,也就是说找不到属性的setter方法,那么不会再去找实例变量,所以会输出上面的结果。所以name输出结果就是null。
+ (BOOL)accessInstanceVariablesDirectly 返回YES实例
accessInstanceVariablesDirectly 默认返回YES。
实例1 self.name
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic,strong)NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self setValue:@"小明" forKey:@"name"];
NSLog(@"self.name = %@", self.name);
}
实例2 name
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
{
NSString *name;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setValue:@"小明" forKey:@"name"];
NSLog(@"name = %@", name);
}
打印数据:
2018-05-16 14:53:45.648667+0700 KVC[22042:467903] name = 小明
实例3 _name
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
{
NSString *_name;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self setValue:@"小明" forKey:@"name"];
NSLog(@"_name = %@", _name);
}
打印数据:
2018-05-16 14:57:03.714227+0700 KVC[22107:470305] _name = 小明
实例4 _isName
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
{
NSString *_isName;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self setValue:@"小明" forKey:@"name"];
NSLog(@"_isName = %@", _isName);
}
打印数据:
2018-05-16 14:58:24.632017+0700 KVC[22153:471747] _isName = 小明
实例5 isName
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
{
NSString *isName;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self setValue:@"小明" forKey:@"name"];
NSLog(@"isName = %@", isName);
}
打印数据:
2018-05-16 15:00:53.388740+0700 KVC[22223:474376] isName = 小明
下面就是我们这个属性和实例变量的调用顺序。
- self.name
- _name
- _isName
- name
- isName
上面的顺序亲测,是按照所列顺序执行的。