• KVO 使用及原理


    KVO的基本原理大概是这样的

      当一个对象被观察时, 系统会新建一个子类NSNotifying_A ,在子类中重写了对象被观察属性的 set方法,  并且改变了该对象的 isa 指针的指向(指向了新建的子类) , 当属性的值发生改变了, 会调用子类的set方法, 然后发出通知

    一. KVO 的基本使用

    给_person对象 添加观察者self, 当person对象的name的值发生改变的时候, 会触发observer方法

    _person = [Person new]; p.name = @"oldName"; 
    //添加观察者
    // [p addObserver:self forKeyPath:@"dog" options:NSKeyValueObservingOptionNew context:nil];
    [_person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];

    // 所观察的对象的keyPath 发生改变的时候, 会触发
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        NSLog(@"%@",keyPath);
        NSLog(@"%@",change);
        
        
    }

    二.  当keyPath 为对象时, 改对象有许多属性, 怎么办?

     在person类中,重写这个方法, 设置需要观察的属性 , 注意:"_dog.level"

    //返回一个容器, 里面放的是NSString类型
    + (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
        
        NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
        //观察dog对象中的所有属性
        if ([key isEqualToString:@"dog"]) {
            keyPaths = [keyPaths setByAddingObjectsFromArray:@[@"_dog.level",@"_dog.age"]];
        }
         
        return keyPaths;
    }

    三. 手动触发KVO

    系统默认该对象的所有属性 都能被观察到 ,重写下面方法, 可以单独设置某个属性不能被观察

    //默认 yes, 默认自动观察所有属性
    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
        return YES;
    }
    //返回NO, 则不能被默认观察到name
    + (BOOL)automaticallyNotifiesObserversOfName{ return NO; } + (BOOL)automaticallyNotifiesObserversOfAge{ return YES; }

    当 name 发生改变的时, 手动触发, 发送通知

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        //手动发通知
        //即将改变(发一次通知)
        [_person willChangeValueForKey:@"name"];
         _person.name = @"dddd";
         //已经改变(发一次通知),一共发了两次通知
        [_person didChangeValueForKey:@"name"];
    }

    四. 自定义KVO

    根据kvo的原理, 可以自定义一个kvo, 建一个NSObject的分类, 添加方法

    @interface NSObject (XSKVO)
    
    - (void)xs_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
    
    @end

    通过runtime的方式, 动态创建一个类, 并给该类添加方法

    #import "NSObject+XSKVO.h"
     #import <objc/runtime.h>
    
    @implementation NSObject (XSKVO)
    
    - (void)xs_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
        
        //1.新建一个类
        NSString *className = [@"XSKVO" stringByAppendingString: NSStringFromClass([self class])];
        Class newClass = objc_allocateClassPair([self class], className.UTF8String, 0);
         //注册类
        objc_registerClassPair(newClass);
        //2.修改 调用者类型
        object_setClass(self, newClass);
        
        //3.给子类添加set方法(子类里面没有set方法的)
        //OC方法:方法编号SEL ,方法实现IMP
        class_addMethod(newClass, @selector(setName:), xssetName, "");
       
    }
    
    /*
     隐藏的参数:
     self  方法的调用者
     _cmd  方法的编号
     
     */
    void xssetName(id self,SEL _cmd, NSString *newName){
        NSLog(@"自定义的实现%@",newName);
        //方案一:通过消息机制 发送消息 -observeValueForKeyPath
        
        
    }
    
    @end

    五. 其他

    关于容器类(如:NSMutableArray)的观察, 当通过addObject: 向数组中添加对象, 不会触发KVO, 因为并没有触发set方法,

    解决方法: 通过KVC 方法 - mutableArrayValueForKey:

  • 相关阅读:
    (SenchaTouch+PhoneGap)开发笔记(2)开发环境搭建二
    Sql语句复习
    冒泡排序
    微信开发订阅号(ASP.NET MVC4+jquery mobile+AppHarbor发布)
    Ext4 ComboBox组件使用
    ExtJs 进度条(轮询)
    如何替换掉.net toolStrip控件溢出按钮背景图
    easyui-menu 宽度自适应
    Python之入门学习
    servlet和filter的区别
  • 原文地址:https://www.cnblogs.com/daxueshan/p/8313318.html
Copyright © 2020-2023  润新知