KVO的基本原理:
1 在Apple中的API文档中如下:
Automatic key-value observing is implemented using a technique called isa-swizzling… When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class …
2 KVO的基本原理:
1 在前面的文章提到过KVO是基于runtime机制实现的iOS Runtime概念归纳 - 简书
2 当某个类的对象第一次被观察时,系统会在运行期间创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter方法,派生类在被重写的setter方法内实现真正的通知机制,若Class类,那么生成的派生类为NSKVONotifying_Class
3 键值观察通知依赖于NSObject的两个方法:willChagreValueForKey:和didChargeValueForKey,在第一个被观察者属性发送改变之前,willChagreValueForKey方法一直被调用,这就会记录旧的值,而当改变时,调用didCharegeValueForKey调用,继而调用 observeValueForKey:ofObject:change:context:
3 KVO的基本使用
(1) 被观察者类:
KVOModel.h
#define KVOID @"name"
@interface KVOModel : NSObject
// 观察KVOModel的name属性
@property(nonatomic,copy)NSString *name;
@end
KVOModel.m
import "KVOModel.h"
#import <objc/runtime.h>
@implementation KVOModel
static const char KVOModelKey = '\0';
- (void)setName:(NSString *)name
{
[self willChangeValueForKey:KVOID];
objc_setAssociatedObject(self, &KVOModelKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self didChangeValueForKey:KVOID];
}
- (NSString *)name
{
return objc_getAssociatedObject(self, &KVOModelKey);
}
(2)观察者类
#import "ViewController.h"
#import "KVOModel.h"
//NSString *const kv0Offset = @"name";
@interface ViewController ()
@property(nonatomic,strong)KVOModel *model;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.model = [KVOModel new];
self.model.name = @"kvo1";
// 注册观察者
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.model addObserver:self forKeyPath:KVOID options:options context:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self removeObservers];
}
/**
keypath:属性名称
object:被观察者对象
charge:变化前后值都在change中
context:注册时候,传过来的值
**/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"keypath=%@, dic==%@",keyPath,change);
}
- (IBAction)kvoClicktest:(id)sender {
self.model.name = @"kvo2";
}
- (void)removeObservers
{
// 移除监听
[self removeObserver:self forKeyPath:KVOID];
}
- (void)dealloc
{
[self removeObservers];
}
网友评论