KVO 基本使用
KVO
: Key-Value Observing
,俗称键值观察,可以监听到某个属性的改变.
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic,strong) Person *per;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
Person *p = [Person new];
p.age = 10;
self.per = p;
NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.per addObserver:self forKeyPath:@"age" options:option context:nil];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.per.age = 20;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"监听到%@的%@ 值改变了----%@",object,keyPath,change);
}
- (void)dealloc{
[self.per removeObserver:self forKeyPath:@"age"];
}
@end
输出:
监听到<Person: 0x60000134c3e0>的age 值改变了----{
kind = 1;
new = 20;
old = 10;
}
KVO 本质分析
image.png- 没有使用KVO的对象的,其实例对象的
isa
是没有变化的(指向类对象); - 使用KVO的对象,其
isa
指向的是NSKVONotifying_person
(这是个利用runtime动态创建的一个类,继承Person
)
分析:当per2
调用 setAge
方法 的时候,本质是通过其isa
找到其class
对象person
,在class
对象中找到该方法并调用.而当per1
调用setAge
的时候,由于其isa
指向的是NSKVONotifying_person
这个类对象.会在这个类里面找到该方法并调用(这里其实调用的是Foudation
框架的_NSSetIntValueAndNotify
)
#import "NSKVONotifying_Person.h"
@implementation NSKVONotifying_Person
- (void)setAge:(int)age{
_NSSetIntValueAndNotify();
}
//伪代码
void _NSSetIntValueAndNotify()
{
[self willChangeValueForKey:@"age"];
[super setAge:age];
[self didChangeValueForKey:@"age"];
}
-(void)didChangeValueForKey:(NSString *)key{
//通知监听器
[self observeValueForKeyPath:key ofObject:self change:nil context:nil];
}
@end
本质验证
image.png image.png小细节:
NSLog(@"%@--%@",object_getClass(self.per1),[self.per1 class]);
11:05:59.274011+0800 kvo[64841:2381561] NSKVONotifying_Person--Person
这里Apple重写class方法来屏蔽了NSKVONotifying_Person
的存在.
补充:
- (void)printMethodNameOfClass:(Class)cls{
unsigned int count ;
Method *methodList = class_copyMethodList(cls, &count);
NSMutableString *mStr = [NSMutableString string];
for (int i = 0; i < count; i++) {
Method method = methodList[i];
NSString *methodName = NSStringFromSelector( method_getName(method));
[mStr appendFormat:@"%@,",methodName];
}
free(methodList);
NSLog(@"%@",mStr);
}
11:20:07.268906+0800 kvo[65186:2398429] setAge:,class,dealloc,_isKVOA,
面试:
iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么)
- 利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类.
- 当修改对象属性的时候,会调用
Foundation
的_NSSetxxxValueAndNotify
函数- willChangeValueForKey
- 父类的setter
- didChangeValueForKey
- 最后内部通知监听器结果改变.
如何手动触发KVO?
- willChangeValueForKey
- didChangeValueForKey
直接修改成员变量会触发KVO吗?
- 只有通过setter方法才会触发.
KVC key-Value Coding 键值编码
基本使用
Person *p1 = [Person new];
p1.catobjc = [cat new];
[p1 setValue:@"tom" forKeyPath:@"catobjc.name"];
[p1 setValue:@1 forKey:@"age"];
NSLog(@"%@---%@",[p1 valueForKey:@"age"],[p1 valueForKeyPath:@"catobjc.name"]);
输出: 13:19:17.167290+0800 kvo[67940:2505342] 1---tom
setValue:forkey
原理:
valueForKey
原理
KVC 会触发KVO 因为KVC 其实相当于调用了
willChangeValueForKey
和didChangeValueForKey
网友评论