项目 Demo 下载
解决的问题:
1:iOS KVO的本质
2: 如何手动触发 KVO
KVO:键值监听. 监听某个对象属性值的改变
第一步: 对象监听属性的改变
[self.model addObserver:self forKeyPath:@"name" options: NSKeyValueObservingOptionNew context:nil];
第二步: 添加对象属性改变的回调方法
//回调
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:@"name"]) {
NSLog(@"change = %@",change);
}
NSLog(@"监听到了 OC");
}
oc版
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor yellowColor];
// Do any additional setup after loading the view.
self.model = [KVOModel new];
//添加观察者
[self.model addObserver:self forKeyPath:@"name" options: NSKeyValueObservingOptionNew context:nil];
self.model.name = @"adsf ";
NSLog(@"name = %@",self.model.name);
}
//回调
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:@"name"]) {
NSLog(@"change = %@",change);
}
NSLog(@"监听到了 OC");
}
//销毁的时候一定要移除
-(void)dealloc
{
[self.model removeObserver:self forKeyPath:@"name"];
}
关键点:
//未被监听前
(lldb) po self.model.isa
KVOModel
Fix-it applied, fixed expression was:
self.model->isa
//监听后
(lldb) po self.model.isa
NSKVONotifying_KVOModel
Fix-it applied, fixed expression was:
self.model->isa
(lldb)
通过上面我们可以发现. 当 model 被监听后,它的 isa 指针指向了另外一个类.是 model 类的子类->NSKVONotifying_KVOModel .它是通过 runtime 动态创建的一个类
通过新建的类 调用 setName 的方法会触发下面两个方法
1:willChangeValueForKey
2:didChangeValueForKey
通过上面的方法来达到回调. 这也就是为什么,监听了之后会触发回调方法的实现
同时: 如果想要手动触发 KVO. 那么调用上面两个方法即可手动触发
[self.model willChangeValueForKey:@"name"];
[self.model didChangeValueForKey:@"name"];
swift 版本
跟 oc 版一样大致一样. 但是有一点需要注意
需要监听的属性需要加上 @objc dynamic 表示创建一个被观察的对象
class Person: NSObject {
@objc dynamic var name = "张三"
var age = 10
}
代码:
class ViewController: UIViewController {
let person = Person()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
// Do any additional setup after loading the view.
person.addObserver(self, forKeyPath: "name", options: [NSKeyValueObservingOptions.new,NSKeyValueObservingOptions.old], context: nil)
print(person.name)
let jumpBtn = UIButton()
jumpBtn.setTitle("OC 版本", for: .normal)
jumpBtn.addTarget(self, action: #selector(click), for: .touchUpInside)
view.addSubview(jumpBtn)
jumpBtn.frame = .init(x: 200, y: 200, width: 200, height: 20)
}
@objc func click() {
let vc = OCKvoViewController()
present(vc, animated: true, completion: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("\(person.name)")
person.name = "张三改"
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("监听到了")
}
deinit {
person.removeObserver(self, forKeyPath: "name", context: nil)
}
}
class Person: NSObject {
@objc dynamic var name = "张三"
var age = 10
}
网友评论