美文网首页
iOS 深入了解KVO 本质/使用

iOS 深入了解KVO 本质/使用

作者: iOS刘耀宗 | 来源:发表于2021-06-20 12:43 被阅读0次

    项目 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
    }
    

    相关文章

      网友评论

          本文标题:iOS 深入了解KVO 本质/使用

          本文链接:https://www.haomeiwen.com/subject/vaojyltx.html