美文网首页iOS
iOS-KVO(四) 自定义KVO+Block

iOS-KVO(四) 自定义KVO+Block

作者: 厦门_小灰灰 | 来源:发表于2019-07-05 23:55 被阅读0次

    iOS-KVO(一) 基本操作
    iOS-KVO(二) 使用注意点
    iOS-KVO(三) 窥探底层实现
    iOS-KVO(四) 自定义KVO+Block

    这一节,将自定义KVO,并且通过Block进行返回属性的变化。只是验证大致流程,并未做一些判断,比如传入key是空之类的。

    我们还是按照以下流程来讲代码:

    KVO主要流程:

    1. 创建一个中间类并创建class方法,返回父类;
    2. isa指针指向中间类;
    3. 重写setter方法;
      另外自己另外写了一个类(LHObserver),来存放观察者,观察的key,以及block。
      以下的代码全部都在一个类别中实现:
    NSObject+BlockKVO
    

    先简单看下 LHObserver这个类

    @interface LHObserver : NSObject
    
    @property (nonatomic, weak) NSObject *observer;
    @property (nonatomic, copy) NSString *observerKey;
    @property (nonatomic, weak) id context;
    @property (nonatomic, copy) LH_ObserverHandler observerHandler;
    
    @end
    
    @implementation LHObserver
    
    - (instancetype)initWithObersver:(NSObject *)observer
                         observerKey:(NSString *)observerKey
                     observerHandler:(LH_ObserverHandler)observerHandler
                             context:(nullable id)context
    {
        self = [super init];
        
        if ( self ) {
            
            self.observer = observer;
            self.observerKey = observerKey;
            self.observerHandler = observerHandler;
            self.context = context;
            
        }
        
        return self;
    }
    
    @end
    

    其中LH_ObserverHandler这个block为:

    /**
     监听属性变化
    
     @param observerObject 观察者
     @param observerKey 观察的名称
     @param object 被观察者
     @param change 监听返回的信息
     @param context 上下文
     */
    typedef void(^LH_ObserverHandler)(id observerObject, NSString *observerKey, id object, NSDictionary<NSKeyValueChangeKey,id> *change, id context);
    

    接下来开始KVO的流程代码

    创建一个中间类

    • 拼接setter方法
    /**
     获取setter方法
    
     @param observerKey 观察的名称
     @return setter SEL
     */
    - (SEL)setterSELWithObserverKey:(NSString *)observerKey
    {
        //比如observerKey的值为name,那么setter的方法名称就是 setName:  冒号不要忘记咯~
        return NSSelectorFromString([NSString stringWithFormat:@"set%@:", [observerKey capitalizedString]]);
    }
    
    • 创建中间类
    /**
     创建中间类
    
     @param originalClass 原先的类
     @return 中间类
     */
    - (Class)createKVOClassWithOriginalClass:(Class)originalClass
    {
        //获取原先类的名称
        NSString *originalClassName = [NSString stringWithUTF8String:object_getClassName(originalClass)];
        //拼接中间类的名称
        NSString *kvoClassName = [NSString stringWithFormat:@"%@%@", KVO_CLASS_NAME_PREFIX, originalClassName];
        
        //判断中间类是否存在,如果存在直接返回即可
        Class kvoClass = objc_getClass(kvoClassName.UTF8String);
        
        if ( kvoClass ) {
            return kvoClass;
        }
        
        //不存在的话,就开始初始化与注册类
        kvoClass = objc_allocateClassPair([self class], kvoClassName.UTF8String, 0);
        objc_registerClassPair(kvoClass);
        
        //添加class的方法,返回父类
        Method classMethod = class_getInstanceMethod(originalClass, @selector(class));
        class_addMethod(kvoClass, NSSelectorFromString(@"class"), (IMP)getKVOClass, method_getTypeEncoding(classMethod));
        NSLog(@"%d", [self hasSelectorWithClass:kvoClass sel:@selector(class)]);
        
        return kvoClass;
    }
    
    /**
     返回父类
    
     @param self self
     @param _cmd _cmd
     @return 父类
     */
    Class getKVOClass(id self, SEL _cmd)
    {
        return class_getSuperclass(object_getClass(self));
    }
    

    isa指针指向中间类

        //1.创建一个中间类, 2.并且isa指针指向中间类;
        Class kvoClass = [self createKVOClassWithOriginalClass:[self class]];
        
        //2.isa指针指向中间类;
        object_setClass(self, kvoClass);
    

    重写setter方法

    • 先判断当前类是否已经存在setter方法了,如果有的话就不在添加
    /**
     是否已经存在sel方法
    
     @param cls 类
     @param sel sel方法
     @return YES:存在  NO:不存在
     */
    -(BOOL)hasSelectorWithClass:(Class)cls sel:(SEL)sel
    {
        unsigned int outCount = 0;
        Method *methodList = class_copyMethodList(cls, &outCount);
        for (int i = 0; i < outCount; ++i) {
            Method method = methodList[i];
            SEL selector = method_getName(method);
            if ( selector == sel ) {
                free(methodList);
                return YES;
            }
        }
        free(methodList);
        return NO;
    }
    
    • setter方法的实现
        //3.重写setter方法;
        //(1)获取setter方法 - 上面已经先获取了
        //(2)判断中间类是否存在setter方法
        if ( ![self hasSelectorWithClass:kvoClass sel:setter] ) {
            //(3)不存在就添加setter方法
            class_addMethod(kvoClass, setter, (IMP)setterIMP, method_getTypeEncoding(setterMethod));
        }
    
    //存储LHObserver的对象的可变数组
        NSMutableArray *observers = objc_getAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY);
        if ( !observers ) {
            observers = [NSMutableArray array];
        }
        //初始化LHObserver的实例对象
        LHObserver *observerInfo = [[LHObserver alloc] initWithObersver:observer
                                                          observerKey:observerKey
                                                      observerHandler:observerHandler
                                                              context:context];
        [observers addObject:observerInfo];
        
        objc_setAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY, observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    void setterIMP(id self, SEL _cmd, id newValue)
    {
        /*
         1.willChangeValueForKey
         2.[super setter]
         3.didChangeValueForKey
         */
    
        //获取getter方法名称
        NSString *getterFuncName = objc_getAssociatedObject(self, &LH_GETTER_FUNCTION_NAME);
        
        //先将当前中间类保存下来
        Class kvoClass = object_getClass(self);
        
        //先将isa指针指向父类,注意之前已经实现过class方法
        Class cls = [self class];
        object_setClass(self, cls);
        
        //获取旧数据
        id oldValue = ((id(*)(id, SEL))objc_msgSend)(self, NSSelectorFromString(getterFuncName));
        
        //调用父类的setter
        ((id(*)(id, SEL, id))objc_msgSend)(self, _cmd, newValue);
        
        //拼凑数据
        NSMutableDictionary *change = [@{} mutableCopy];
        //这里要根据自己的需求来定,如果对应的value是空的话,直接赋值空字符串
        if ( oldValue ) {
            change[NSKeyValueChangeOldKey] = oldValue;
        } else {
            change[NSKeyValueChangeOldKey] = @"";
        }
        
        if ( newValue ) {
            change[NSKeyValueChangeNewKey] = newValue;
        } else {
            change[NSKeyValueChangeNewKey] = @"";
        }
        
        //再将isa指针指向中间类
        object_setClass(self, kvoClass);
        
        NSMutableArray *observers = objc_getAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY);
        if ( !observers || observers.count <= 0 ) {
            return;
        }
        //循环获取observerObject,然后执行block
        for (LHObserver *observerObject in observers) {
            if ( [observerObject.observerKey isEqualToString:getterFuncName] ) {
                if ( observerObject.observerHandler ) {
                    dispatch_async(dispatch_get_global_queue(0, 0), ^{
                        //这里是在子线程
                        observerObject.observerHandler(observerObject.observer, observerObject.observerKey, self, change, observerObject.context);
                    });
                }
                break;
            }
        }
    }
    

    移除观察者

    /**
     移除观察者
     
     @param observer 观察者
     @param observerKey 观察的名称
     */
    - (void)lh_removeObserver:(NSObject *)observer forObserverKey:(NSString *)observerKey
    {
        NSMutableArray *observers = objc_getAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY);
        if ( !observers ) {
            return;
        }
        
        LHObserver *tempObserverObject = nil;
        for (LHObserver *observerObject in observers) {
            if ( observerObject.observer == observer && [observerObject.observerKey isEqualToString:observerKey] ) {
                tempObserverObject = observerObject;
                break;
            }
        }
        
        //移除
        if ( tempObserverObject ) {
            [observers removeObject:tempObserverObject];
        }
    }
    
    简单使用
    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface Person : NSObject
    
    @property (nonatomic, copy) NSString *name;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    #import "ViewController.h"
    #import "Person.h"
    #import "NSObject+BlockKVO.h"
    
    @interface ViewController ()
    
    @property (nonatomic, strong) Person *p1, *p2;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.p1 = [Person new];
        
        [self.p1 setName:@"1"];
        
        [self.p1 lh_addObserver:self
                 forObserverKey:@"name"
                        options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld
                        context:@"123"
                observerHandler:^(id  _Nonnull observerObject, NSString * _Nonnull observerKey, id  _Nonnull object, NSDictionary<NSKeyValueChangeKey,id> * _Nonnull change, id  _Nonnull context) {
                    NSLog(@"p1 %@, context:%@", change, context);
                }];
        
        
        [self.p1 setName:@"3"];
        
        [self.p1 lh_removeObserver:self forObserverKey:@"name"];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        [self.p1 setName:@"hui"];
    }
    
    
    - (void)dealloc
    {
        NSLog(@"%@ dealloc", [self class]);
    }
    
    @end
    

    打印结果:

    2019-07-05 23:52:53.765 KVODemo[5158:175811] {
        new = 3;
        old = 1;
    }, context:123
    
    

    这章主要都是代码,将之前说的底层原理的流程,自己通过代码简单的实现,代码还有很多可以改进的地方。
    上面代码 传送门

    KVO over!

    相关文章

      网友评论

        本文标题:iOS-KVO(四) 自定义KVO+Block

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