美文网首页
04. KVO使用,原理,本质

04. KVO使用,原理,本质

作者: ProfessorFan | 来源:发表于2021-01-05 20:54 被阅读0次

    问题

    1. KVO日常使用
    2. KVO原理(KVO本质是什么)
    3. 如何手动触发KVO
    4. 直接修改成员变量会触发KVO吗
    5. KVO图解
    6. 获取一个Class 对象的方法列表

    答案

    1. KVO日常使用
    #import <Foundation/Foundation.h>
    
    @interface MJCat : NSObject
    @property (assign, nonatomic) int weight;
    @end
    
    @interface MJPerson : NSObject
    @property (assign, nonatomic) int age;
    
    @property (assign, nonatomic) MJCat *cat;
    @end
    
    @implementation MJCat
    
    @end
    
    @implementation MJPerson
    
    //- (void)setAge:(int)age
    //{
    //    _age = age;
    //
    //    NSLog(@"setAge: - %d", age);
    //}
    
    @end
    
    @interface MJObserver : NSObject
    
    @end
    
    @implementation MJObserver
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        NSLog(@"observeValueForKeyPath - %@", change);
    }
    
    @end
    
    
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            MJObserver *observer = [[MJObserver alloc] init];
            MJPerson *person = [[MJPerson alloc] init];
            
            // 添加KVO监听
            [person addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
            
            // 通过KVC修改age属性
            [person setValue:@10 forKey:@"age"];
            
            // setAge:
            
            // 移除KVO监听
            [person removeObserver:observer forKeyPath:@"age"];
            
            
    //        person.age = 10;
            
    //        NSLog(@"%@", [person valueForKey:@"age"]);
    //        NSLog(@"%@", [person valueForKeyPath:@"cat.weight"]);
            
            
    //        NSLog(@"%d", person.age);
            
    //        [person setValue:[NSNumber numberWithInt:10] forKey:@"age"];
    //        [person setValue:@10 forKey:@"age"];
    //        person.cat = [[MJCat alloc] init];
    //        [person setValue:@10 forKeyPath:@"cat.weight"];
            
    //        NSLog(@"%d", person.age);
            
        
        }
        return 0;
    }
    
    1. KVO原理(KVO本质是什么)
    • 利用RuntimeAPI动态生成一个子类,并且让instance(实例对象)对象的isa指向全新的子类
    • 在全新的子类中重写了父类的set方法,当我们修改instance对象的属性的时候,会调用Foundation 的_NSSetXXXValueAndNotify函数
    • 源码(set方法)实现流程大概是这样的
      a: 第一步先调用willChangeValueForKey:方法
      b: 第二步调用父类的setter方法
      c: 第三步调用didChangeValueForKey:方法,在方法内部会触发监听器(Oberser)的监听方法(observeValueForKeyPath:ofObject:change:context:)
    1. 如何手动触发KVO
    • 根据KVO原理我们很容易手动触发KVO
      1: 第一步实例对象手动调用willChangeValueForKey:方法
      2: 第二步实例对象手动调用didChangeValueForKey:方法
    1. 直接修改成员变量会触发KVO吗
    • 直接修改成员变量不会触发KVO,因为直接修改成员变量不会走新子类重写的setter方法,不会去触发willChangeValueForKey: 和 didChangeValueForKey:这两个方法
    1. KVO图解


      KVO图解.png
    2. 获取一个Class 对象的方法列表

    #import <objc/runtime.h>
    
    - (void)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);
        
        // 打印方法名
        NSLog(@"%@ %@", cls, methodNames);
    }
    
    # 测试
    
      self.person1 = [[MJPerson alloc] init];
      self.person1.age = 1;
        
      self.person2 = [[MJPerson alloc] init];
      self.person2.age = 2;
        
        // 给person1对象添加KVO监听
      NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
      [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
        
      [self printMethodNamesOfClass:object_getClass(self.person1)];
      [self printMethodNamesOfClass:object_getClass(self.person2)];
    # 结果
    2021-01-05 19:32:33.868603+0800 Interview01[4816:567497] NSKVONotifying_MJPerson setAge:, class, dealloc, _isKVOA,
    2021-01-05 19:32:33.868817+0800 Interview01[4816:567497] MJPerson setAge:, age,
    

    相关文章

      网友评论

          本文标题:04. KVO使用,原理,本质

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