一般的面试题喜欢问kvc和kvo的区别,实际上这两者关系真心不大,能够联系上的就是都是键值对的形式存在的(k-v),kvc调用会调用到kvo(如果设置过监听),都是使用的set函数
其它的就基本没啥关系了
kvo的本质:
#import "ViewController.h"
#import "Person.h"
#import <objc/runtime.h>
@interface ViewController ()
@property (nonatomic,strong)Person *person;
@property (nonatomic,strong)Person *person1;
@end
@implementation ViewController
-(void)printMethodForClass:(Class)cls
{
unsigned int count ;
Method *methods = class_copyMethodList(cls, &count);
for(int i = 0; i < count; i++)
{
Method method = methods[i];
NSLog(@"%@" , NSStringFromSelector(method_getName(method)));
}
free(methods);
}
- (void)viewDidLoad {
[super viewDidLoad];
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld;
self.person = [[Person alloc] init];
self.person1 = [[Person alloc] init];
NSLog(@"监听之前%p,%p",[self.person methodForSelector:@selector(setMoney:)],[self.person1 methodForSelector:@selector(setMoney:)]); //监听之前0x10ca74cf0,0x10ca74cf0
NSLog(@"监听之前元类对象%p,%p",object_getClass(object_getClass(self.person)),
object_getClass(object_getClass(self.person1))); //监听之前元类对象0x10ca7c800,0x10ca7c800
[self.person addObserver:self forKeyPath:@"money" options:options context:nil];
[self.person addObserver:self forKeyPath:@"age" options:options context:@"123"];
NSLog(@"监听之后%p,%p",[self.person methodForSelector:@selector(setMoney:)],[self.person1 methodForSelector:@selector(setMoney:)]); //监听之后0x7fff207bf79f,0x10ca74cf0
NSLog(@"监听之后元类对象%p,%p",object_getClass(object_getClass(self.person)),
object_getClass(object_getClass(self.person1))); //监听之后元类对象0x600000c28510,0x10ca7c800
[self printMethodForClass:object_getClass(self.person)];
// setAge:
// setMoney:
// class
// dealloc
// _isKVOA
NSLog(@"Person1");
[self printMethodForClass:object_getClass(self.person1)];
// setMoney:
// money
// Do any additional setup after loading the view.
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.person.money = 30;
}
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.person.age = 40;
self.person1.age = 30;
}
-(void)dealloc
{
[self.person removeObserver:self forKeyPath:@"money"];
[self.person removeObserver:self forKeyPath:@"age"];
}
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context
{
NSLog(@"change%@",change);
NSLog(@"keypath%@",keyPath);
}
可以看的出来,person在设置之前和设置之后,连isa的指向地址都变了,而person1没设置过所以没变化,实际上的kvo设置监听之后会创建一个新类,这个类会继承Person然后重写Person的set函数,然后调用自己的- (void)willChangeValueForKey:(NSString *)key
-(void)didChangeValueForKey:(NSString *)key 等等这些方法,然后又接受过本地传入的self调用
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context,这个逻辑就很像delegate模式,但又不一样
以此来实现值得监听
kvc的本质:
kvc本质就是找set get方法,比如
Person *person = [[Person alloc] init];
[person setValue:@20 forKey:@"age"];
NSLog(@"%@",[person valueForKey:@"age"]);
kvc是setvalue和valueForkey,本身是键值对形式没啥好说的,原理就是setvalue去找set函数,valueforkey去找对应的get函数,比如传入的是age 会寻找getAge age _age isAge这些,优先找的是getAge,set也差不多...但我想了下这么写代码效率不会很低么,一方面要搜索多个,另一方面敲错个字母,又得排查半天的错误