• KVO


    首先我们先介绍KVO的底层实现原理:

        我们注册监听的时候,会对注册者动态的创建一个子类对象,然后底层找方法的的isa指针就变成指向新创建的子类对象。当改变注册对象某个属性的时候,

    就重写属性的set方法来进行监听。这么说可能理解上不是很明白,下面我们结合代码来分析:

    我们常见一个Person对象是继承与NSObject,有一个name属性。我们把Person的类对象的name属性注册给ViewController监听。

        self.p = [[Person alloc] init];

        //此行注册监听后,p由Person类变成NSKVONotyfing_Person类。

        [self.p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

     

    当我们点击屏幕后,改变self.p的name属性值:

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

        self.p.name = @"小明";

    }

    这时候就会调用self.p的- (void)setName:(int)name;方法,  注意:此时self.p属于NSKVONotyfing_Person类对象。

    name属性改变后,随即调用KVO的监听方法中:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {

        NSLog(@"%@监听到%@属性的改变为%@",

              object,keyPath,change);

    }

    以上就是KVO底层原理。
     
    下面我们就来自定义一个KVO,步骤:
    1.同样的创建一个Person类,有两个属性name、age。
    2.给NSObject创建一个分类名字叫KVO,添加一个方法:

    -(void)AW_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

    3.ViewController初始化Person以及时间监听(注意添加监听的方法是自己自定义的):

    [self.p AW_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];

     
    ViewController中的方法时刻保持监听状态:

    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {

     
    }
     
    4.在分类NSOject+KVO.m文件实现相关处理,如下:

    -(void)AW_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context

    {

        //动态添加一个类

        NSString *oldClassName = NSStringFromClass([self class]);

        NSString *newClassName = [@"AWKVO_" stringByAppendingString:oldClassName];

        const char * newName = [newClassName UTF8String];

        Class myclass = objc_allocateClassPair([self class], newName, 0);

        

        //添加setter方法,相当于重写setter方法, "v@:i" 含义 @: id   : SEL     v : void  

          OC(消息发送机制),方法由两部分组成,方法编号@selector和方法实现(imp方法指针),先找方法编号再得到方法的指针,再执行方法的代码块。

        class_addMethod(myclass, @selector(setAge:), (IMP)setAge, "v@:i");

        

        //注册新添加的这个类

        objc_registerClassPair(myclass);

        

        //修改被观察这的isa指针,isa指针指向Person类改成指向myclass这个类

        object_setClass(self, myclass);

        

        //将观察者的属性保存到当前类里面去

        objc_setAssociatedObject(self, (__bridge const void *)@"objc", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    }

    //相当于重写父类的方法

    void setAge(id self, SEL _cmd, int age) {

        

        //保存当前类

        Class myclass = [self class];

        

        //将self的isa指针指向父类

        object_setClass(self, class_getSuperclass([self class]));

        

        //调用父类

        objc_msgSend(self, @selector(setAge:),age);

        

        //拿出观察者

        objc_getAssociatedObject(self, (__bridge const void *)@"objc");

        

        //通知观察者

        objc_msgSend(objc,@selector(observeValueForKeyPath:ofObject:change:context:),self,age,nil,nil);

        

        //改为子类

        object_setClass(self, myclass);

    }

     
    这样就可以回调到ViewContoller中的监听方法observeValueForKeyPath:ofObject:change:context:中去,而且相关的值也传递过去了。

    总结自定义一个KVO思路:

    1.自定义一个类,继承 [self class]的一个子类

    2.重写父类的属性setter方法

    3.调用observeValueForKeyPath:ofObject:change:context:方法,回调到ViewController中去。

    附加:

    苹果为什么要用子类(就是C语言创建的那个子类)监听setter方法,而不用分类(Person+AWKVO)呢?

    回答:原因是当你用分类监听setter方法的时候,Person类中setter方法就不会走了,这样不好,所以苹果使用了子类监听setter方法。

    C语言创建某一个类的子类:

    //动态添加一个类

    NSString *oldClassName = NSStringFromClass([self class]);

    NSString *newClassName = [@"AWKVO_" stringByAppendingString:oldClassName];

    const char * newName = [newClassName UTF8String];

    Class myclass = objc_allocateClassPair([self class], newName, 0);

     


     
     
  • 相关阅读:
    Chrome cookies folder
    Fat URLs Client Identification
    User Login Client Identification
    Client IP Address Client Identification
    HTTP Headers Client Identification
    The Personal Touch Client Identification 个性化接触 客户识别
    购物车 cookie session
    购物车删除商品,总价变化 innerHTML = ''并没有删除节点,内容仍存在
    453
    购物车-删除单行商品-HTMLTableElement.deleteRow()
  • 原文地址:https://www.cnblogs.com/SimonGao/p/9596484.html
Copyright © 2020-2023  润新知