美文网首页
KVO 学习

KVO 学习

作者: 化二缺 | 来源:发表于2020-05-05 16:14 被阅读0次
    • KVO key-Value Observing 键值监听 ,可以用于监听某个对象属性值的改变

    使用了KVO监听的对象

    • runtime动态创建一个子类


      image.png

    KVO探究

    #import "learnKVOdeepVC.h"
    @interface Person : NSObject
    @property(nonatomic,strong) NSString * name;
    @end
    @implementation Person
    
    @end
    
    @interface learnKVOdeepVC ()
    @property(nonatomic,strong) Person * person1;
    @property(nonatomic,strong) Person * person2;
    @end
    
    @implementation learnKVOdeepVC
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //不设置颜色 touchesBegan 无效 这个需要注意 
        self.view.backgroundColor = [UIColor whiteColor];
        self.person1 = [[Person alloc]init];
        self.person1.name = @"person1";
        
        self.person2 = [[Person alloc]init];
        self.person2.name = @"person2";
        
        
        NSLog(@"person1添加KVO监听之前 - %s %s", object_getClassName(self.person1),
              object_getClassName(self.person2));
        
        
      NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld ;
        [self.person1 addObserver:self forKeyPath:@"name" options:options context:@"123"];
        
        NSLog(@"person1添加KVO监听之后 - %s %s", object_getClassName(self.person1),
              object_getClassName(self.person2));
    }
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [self.person1 setName:@"哈哈"];
        
    }
    
    -(void)dealloc{
        [self.person1 removeObserver:self forKeyPath:@"name"];
    }
    
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        
        NSLog(@"监听到的%@的%@属性值改变了 - %@ - %@",object,keyPath,change,context);
    }
    @end
    

    输出

    2020-05-06 09:49:50.766225+0800 HYZExplore[4119:64877] person1添加KVO监听之前 - Person Person
    2020-05-06 09:49:50.766508+0800 HYZExplore[4119:64877] person1添加KVO监听之后 - NSKVONotifying_Person Person
    2020-05-06 09:49:51.825428+0800 HYZExplore[4119:64877] 监听到的<Person: 0x600002f5a1c0>的name属性值改变了 - {
        kind = 1;
        new = "\U54c8\U54c8";
        old = person1;
    } - 123
    
    

    可见 person1的Class对象不再是Person 而是一个 NSKVONotifying_Person 的类对象

     NSLog(@"person1添加KVO监听之前 - %p %p", [self.person1 methodForSelector:@selector(setName:)],
               [self.person2 methodForSelector:@selector(setName:)]);
     NSLog(@"person1添加KVO监听之后 - %p %p", [self.person1 methodForSelector:@selector(setName:)],
               [self.person2 methodForSelector:@selector(setName:)]);
    

    输出

     person1添加KVO监听之前 - 0x10b804510 0x10b804510
     person1添加KVO监听之后 - 0x7fff258e454b 0x10b804510
    

    可见 person1的方法实现地址变了

    • KVO生成的class对象的isa 指向KVO 生成的class对象的元类,不会指向被观察的对象的元类

    KVO 对象里面的方法

    • class方法
      NSLog(@"person1添加KVO监听之后 - %s %s", object_getClassName(self.person1),
              object_getClassName(self.person2));
     NSLog(@"person1添加KVO监听之后 - %@ %@", [self.person1 class],
                 [self.person2 class]);
    

    输出结果
    NSKVONotifying_Person Person
    Person Person
    是不一样的
    KVO的对象的class方法重写了 是为了隐蔽 NSKVONotifying_Person
    屏蔽内部实现

    探究2

    - (NSString*)printMethodNamesOfClass:(Class)cls
    {
        unsigned int count ;
        //获得方法数组
        Method  * methodlist = class_copyMethodList(cls, &count);
        
    // 存储方法名
        NSMutableString * methodNames = [NSMutableString string];
        
        //遍历所有方法
        for(int i =0 ; i < count ; i++ ){
            //获取方法
            Method method = methodlist[i];
            //获取方法名
            NSString * methodName = NSStringFromSelector(method_getName(method));
            //拼接方法名
            [methodNames appendString: methodName];
            [methodNames appendString:@", "];
        }
        free(methodlist);
        return  methodNames;
    }
    
    
     
       NSString * names = [self printMethodNamesOfClass:[self.person1 class]];
        
        NSLog(@"printMethodNamesOfClass = %@",names);
        
        Class realclass = NSClassFromString([NSString stringWithFormat:@"%s",object_getClassName(self.person1)] );
        
        NSString * names2 = [self printMethodNamesOfClass:
                             realclass];
        
        NSLog(@"printMethodNamesOfClass = %@",names2);
    

    输出结果
    printMethodNamesOfClass = .cxx_destruct, name, setName:,
    printMethodNamesOfClass = setName:, class, dealloc, _isKVOA,
    可见变成kvo对象了

    总结

    1.iOS用什么方法实现对一个对象的KVO?本质是什么?

    1. 利用RuntimeAPI动态生成的一个子类,并且让instance对象的isa指向这个全新的子类
    2. 当修改instance对象属性时,会调用Foundation的 _NSSetXXXValueAndNotify函数

    willChangeValueForKey:
    父类原来的setter
    didChangeValueFoeKey:
    内部会触发监听器(Oberser)的监听方法(-observeValueForKeyPath:ofObject:change:context:)

    2.手动触发KVO?

      [self.person1 willChangeValueForKey:@"name"];
      [self.person1 didChangeValueForKey:@"name"];
    

    3.直接修改成员变量会触发KVO么?

    @interface Person:NSObject 
    {
        @public 
        int  _age ;
    }
    
    Person * p =[ [Person alloc]init];
    
    p->_age = 2;
    

    这种不会触发 因为没有走setter

    相关文章

      网友评论

          本文标题:KVO 学习

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