本文主要是介绍iOS KVO的实现原理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
全称是Key-value observing,翻译成键值观察。提供了一种当其它对象属性被修改的时候能通知当前对象的机制。再MVC大行其道的Cocoa中,KVO机制很适合实现model和controller类之间的通讯。
键值编码(KVC)和键值观察(KVO)是根据isa-swizzling技术来实现的,主要依据runtime的强大动态能力。
当某个类的对象第一次被观察时, 系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter方法。派生类在被重写的 setter 方法中实现真正的通知机制;
系统将这个对象的 isa指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。
新的派生类NSKVONotifying_Person类会重写以下方法:
增加了监听的属性对应的set方法,class,dealloc,_isKVOA。
//重写set方法
新类会重写对应的set方法,是为了在set方法中增加另外两个方法的调用:
1 - (void)willChangeValueForKey:(NSString *)key
2 - (void)didChangeValueForKey:(NSString *)key
其中,didChangeValueForKey:方法负责调用下面这这监听:
1 - (void)observeValueForKeyPath:(NSString *)keyPath
2 ofObject:(id)object
3 change:(NSDictionary *)change
4 context:(void *)context
方法,
这就是KVO实现的原理了!
如果没有任何的访问器方法,-setValue:forKey方法会直接调用:
1 - (void)willChangeValueForKey:(NSString *)key
2 - (void)didChangeValueForKey:(NSString *)key
如果在没有使用键值编码且没有使用适当命名的访问器方法的时候,我们只需要显示调用上述两个方法,同样可以使用KVO!
总结一下,想使用KVO有三种方法:
1)使用了KVC
使用了KVC,如果有访问器方法,则运行时会在访问器方法中调用will/didChangeValueForKey:方法;
没用访问器方法,运行时会在setValue:forKey方法中调用will/didChangeValueForKey:方法。
2)有访问器方法
运行时会重写访问器方法调用will/didChangeValueForKey:方法。
因此,直接调用访问器方法改变属性值时,KVO也能监听到。
3)显示调用will/didChangeValueForKey:方法。
总之,想使用KVO,只要有will/didChangeValueForKey:方法就可以了。
③_isKVOA
这个私有方法估计是用来标示该类是一个 KVO机制声称的类。
@interface ViewController ()
@property (nonatomic,strong)LBPerson*person;//必须设置成属性,防止被销毁,否则报错
@end
@implementation ViewController
- (void)viewDidLoad{
[superviewDidLoad];
LBPerson *person = [[CZPersonalloc]init];
self.person =person;
person.name =@"lambo";
person.age =19;
person.height =180;
//原理:
[personaddObserver:selfforKeyPath:@"height"options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"hello"];
//1.当Person调用上面这个方法时,会动态创建(运行时)NSKVONotifying_LBPerson名称的子类
//2.传入参数 参数1Observer观察者(监听者) 参数2 KeyPath监听的属性 参数3 options枚举(决定将来传递出来的值)新值和旧值 参数4 context携带参数
//3.重写 NSKVONotifying_LBPerson的setHeight:方法
//4.在 NSKVONotifying_LBPerson的setHeight:方法 中 执行一段代码 [self(监听者) observeValueForKeyPath:.....](调用的是下面的方法);
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id>*)change context:(void *)context
//5.更改了 LBPerson的isa指针为NSKVONotifying_LBPerson类型决定:执行该类的方法的时候去哪个类的方法列表中去找(多态是从子类往上级寻找执行方法);
//isaClassNSKVONotifying_LBPerson0x00007fbf2a200690
person.height =178;
}
- (void)dealloc
{//对象销毁的时候移除监听
[self.personremoveObserver:selfforKeyPath:@"height"];
}
//属性只改变时会调用这个方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id>*)change context:(void *)context
{
NSLog(@"%@",change);
//在属性被改变时做事情
}
二.用KVO观察下载进度
-(void)downloadFile{
AFHTTPSessionManager*manager = [AFHTTPSessionManagermanager];
NSURL *url =[NSURLURLWithString:@"http://127.0.0.1/1.mp4"];
NSURLRequest *requst = [NSURLRequestrequestWithURL:url];
/*
1.请求
2.进度
3.下载的目的地需要自己制定下载路径通过block返回
4.完成回调
*/
NSProgress *progress;
[[manager downloadTaskWithRequest:requst progress:&progressdestination:^NSURL *_Nonnull(NSURL *_NonnulltargetPath,NSURLResponse * _Nonnull response) {
//存到temp目录中
NSString *path = [NSTemporaryDirectory()stringByAppendingPathComponent:response.suggestedFilename];
NSURL *url = [[NSURLalloc]initFileURLWithPath:path];
return url;
} completionHandler:^(NSURLResponse*_Nonnull response,NSURL* _Nullable filePath,NSError * _Nullableerror) {
NSLog(@"%@",filePath);
}] resume];
//输出进度 KVO添加观察者
/*
1.谁观察
2.观察的key
3.选项
*/
[progress addObserver:selfforKeyPath:@"fractionCompleted"options:NSKeyValueObservingOptionNewcontext:nil];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id>*)change context:(void *)context{
//观察方法
/*
1.要观察的key
2.要观察的对象
3.新值的变化
4.其他参数
*/
//判断监控的对象类型
if([objectisKindOfClass:[NSProgressclass]]){
NSProgress *progress = object;
//打印进度
// NSLog(@"%f",progress.fractionCompleted);
NSLog(@"%@",progress.localizedDescription);
}
}
这篇关于iOS KVO的实现原理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!