iOS 深入了解KVO 本质/使用

2021-06-20  本文已影响0人  iOS刘耀宗

项目 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
}
上一篇 下一篇

猜你喜欢

热点阅读