KVO & KVC

作者: comsubin | 来源:发表于2019-05-22 10:56 被阅读0次

    KVO 基本使用

    KVO : Key-Value Observing,俗称键值观察,可以监听到某个属性的改变.

    #import "ViewController.h"
    #import "Person.h"
    @interface ViewController ()
    @property (nonatomic,strong) Person *per;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        Person *p = [Person new];
        p.age = 10;
        self.per = p;
        
        NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
        [self.per addObserver:self forKeyPath:@"age" options:option context:nil];
        
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        self.per.age = 20;
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        NSLog(@"监听到%@的%@ 值改变了----%@",object,keyPath,change);
    }
    
    - (void)dealloc{
        [self.per removeObserver:self forKeyPath:@"age"];
    }
    @end
    输出:
    监听到<Person: 0x60000134c3e0>的age 值改变了----{
        kind = 1;
        new = 20;
        old = 10;
    }
    

    KVO 本质分析

    image.png
    • 没有使用KVO的对象的,其实例对象的isa是没有变化的(指向类对象);
    • 使用KVO的对象,其isa指向的是NSKVONotifying_person(这是个利用runtime动态创建的一个类,继承Person)

    分析:当per2调用 setAge方法 的时候,本质是通过其isa找到其class对象person,在class对象中找到该方法并调用.而当per1调用setAge的时候,由于其isa指向的是NSKVONotifying_person这个类对象.会在这个类里面找到该方法并调用(这里其实调用的是Foudation框架的_NSSetIntValueAndNotify)

    #import "NSKVONotifying_Person.h"
    
    @implementation NSKVONotifying_Person
    
    - (void)setAge:(int)age{
        
        _NSSetIntValueAndNotify();
        
    }
    
    //伪代码
    void _NSSetIntValueAndNotify()
    {
        
        [self willChangeValueForKey:@"age"];
        [super setAge:age];
        [self didChangeValueForKey:@"age"];
    }
    
    -(void)didChangeValueForKey:(NSString *)key{
        //通知监听器
        [self observeValueForKeyPath:key ofObject:self change:nil context:nil];
    }
    
    @end
    

    本质验证

    image.png image.png

    小细节:

     NSLog(@"%@--%@",object_getClass(self.per1),[self.per1 class]);
     11:05:59.274011+0800 kvo[64841:2381561] NSKVONotifying_Person--Person
    

    这里Apple重写class方法来屏蔽了NSKVONotifying_Person的存在.
    补充:

    
    - (void)printMethodNameOfClass:(Class)cls{
        unsigned int count ;
        Method *methodList = class_copyMethodList(cls, &count);
        
        NSMutableString *mStr = [NSMutableString string];
        
        for (int i = 0; i < count; i++) {
            Method method = methodList[i];
            NSString *methodName = NSStringFromSelector( method_getName(method));
            [mStr appendFormat:@"%@,",methodName];
        }
        free(methodList);
        NSLog(@"%@",mStr);
    }
     11:20:07.268906+0800 kvo[65186:2398429] setAge:,class,dealloc,_isKVOA,
    

    面试:
    iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么)

    • 利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类.
    • 当修改对象属性的时候,会调用Foundation_NSSetxxxValueAndNotify函数
      • willChangeValueForKey
      • 父类的setter
      • didChangeValueForKey
    • 最后内部通知监听器结果改变.

    如何手动触发KVO?

    • willChangeValueForKey
    • didChangeValueForKey

    直接修改成员变量会触发KVO吗?

    • 只有通过setter方法才会触发.

    KVC key-Value Coding 键值编码

    基本使用

          Person *p1 = [Person new];
            p1.catobjc = [cat new];
            
            [p1 setValue:@"tom" forKeyPath:@"catobjc.name"];
            [p1 setValue:@1 forKey:@"age"];
            
            NSLog(@"%@---%@",[p1 valueForKey:@"age"],[p1 valueForKeyPath:@"catobjc.name"]);
    
    输出: 13:19:17.167290+0800 kvo[67940:2505342] 1---tom
    

    setValue:forkey 原理:

    image.png

    valueForKey原理

    image.png

    KVC 会触发KVO 因为KVC 其实相当于调用了willChangeValueForKeydidChangeValueForKey

    相关文章

      网友评论

          本文标题:KVO & KVC

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