OC的KVC原理
KVC
KVC的全称是Key-Value Coding,俗称"键值编码",可以通过一个key来访问某个属性
常用的API:
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath; - (void)setValue:(id)value forKey:(NSString *)key; - (id)valueForKeyPath:(NSString *)keyPath; - (id)valueForKey:(NSString *)key;
setValue:forKey:原理
编写如下代码验证
#import "ViewController.h" #import <objc/runtime.h> @interface LBPerson : NSObject { int _age; int _isAge; int age; int isAge; } @property (nonatomic, assign) int age; @end @implementation LBPerson - (void)_setAge:(int)age { _age = age; NSLog(@"_setAge"); } - (void)setAge:(int)age { _age = age; NSLog(@"setAge:"); } +(BOOL)accessInstanceVariablesDirectly { // 是否允许直接访问属性 return true; } - (void)willChangeValueForKey:(NSString *)key { [super willChangeValueForKey:key]; NSLog(@"willChangeValueForKey"); } - (void)didChangeValueForKey:(NSString *)key { NSLog(@"didChangeValueForKey Start"); [super didChangeValueForKey:key]; NSLog(@"didChangeValueForKey End"); } @end @interface ViewController () @property (nonatomic, strong) LBPerson *person; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LBPerson *person = [[LBPerson alloc] init]; self.person = person; LBPerson *person1 = [[LBPerson alloc] init]; [person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"test"]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self.person setValue:@20 forKey:@"age"]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context); } - (void)dealloc { [self.person removeObserver:self forKeyPath:@"age"]; }
从上述代码的打印结果,可以验证setValur:forKey:,会顺序调用setKey、_setKey,如果没找到方法,会调用accessInstanceVariablesDirectly。如果该方法返回的是true,会按照优先级给属性赋值age、isAge、_age、_isAge。如果还没有找到,就会报错。调用结果如下图所示:
通过打印也验证了,setValue:forKey:也会触发KVO
valueForKey:原理
- (int)getAge { return 1; } - (int)age { return 2; } - (int)isAge { return 3; } - (int)_age { return 4; }
通过打印可验证,valueForKey的调用顺序getAge、age、isAage、_age