发现手动实现KVO的一个坑

  最近项目开发用到了派生子类的知识,从网上看了一下手动实现KVO的源码,网上发现大家的源码实现差不多。好像都是从一个大佬那里抄来的。
参考博客地址如下:
https://tech.glowing.com/cn/implement-kvo/
https://www.jianshu.com/p/bf053a28accb
源码地址:https://github.com/Jerry4me/JRCustomKVODemo
运行了一下源码发下源码中对与非Object类型的属性进行监听时,会崩溃。比如age,NSInteger类型。具体出问题的代码如下:

/**
 *  重写setter方法, 新方法在调用原方法后, 通知每个观察者(调用传入的block)
 */
static void jr_setter(id self, SEL _cmd, id newValue)
{
    NSString *setterName = NSStringFromSelector(_cmd);
    NSString *getterName = [self getterForSetter:setterName];
    
    
    if (!getterName) {
        NSLog(@"找不到getter方法");
    }
    
    // 获取旧值
    id oldValue = [self valueForKey:getterName];
    
    // 调用原类的setter方法
    
    struct objc_super superClazz = {
        .receiver = self,
        .super_class = class_getSuperclass(object_getClass(self))
    };
    
    ((void (*)(void *, SEL, id))objc_msgSendSuper)(&superClazz, _cmd, newValue);
    
    // 为什么不能用下面方法代替上面方法?
    //    ((void (*)(id, SEL, id))objc_msgSendSuper)(self, _cmd, newValue);
    
    
    // 找出观察者的数组, 调用对应对象的callback
    NSMutableArray *observers = objc_getAssociatedObject(self, JRAssociateArrayKey);
    // 遍历数组
    for (JRObserverInfo *info in observers) {
        if ([info.key isEqualToString:getterName]) {
            // gcd异步调用callback
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                info.callback(info.observer, getterName, oldValue, newValue);
            });
        }
    }
}

崩溃情况如下:


可以看到id其实是objec_object类型的指针,而age是基础数据类型,因此函数调用到这里的时候出现了崩溃。
为了解决这个问题,我祭出了万能指针 void * ,主要原理就是利用void 可以指向基础类型的内存地址。因此可以通过void进行传值。
修改后的代码如下:

/**
 *  重写setter方法, 新方法在调用原方法后, 通知每个观察者(调用传入的block)
 */
static void jr_setter(id self, SEL _cmd, void* newValue)
{
    NSString *setterName = NSStringFromSelector(_cmd);
    NSString *getterName = [self getterForSetter:setterName];
    
    
    if (!getterName) {
        NSLog(@"找不到getter方法");
    }
    
    // 获取旧值
    id oldValue = [self valueForKey:getterName];
    
    // 调用原类的setter方法
    
    struct objc_super superClazz = {
        .receiver = self,
        .super_class = class_getSuperclass(object_getClass(self))
    };
    
    ((void (*)(void *, SEL, void*))objc_msgSendSuper)(&superClazz, _cmd, newValue);
    
    // 为什么不能用下面方法代替上面方法?
    //    ((void (*)(id, SEL, id))objc_msgSendSuper)(self, _cmd, newValue);
    
    
    // 找出观察者的数组, 调用对应对象的callback
    NSMutableArray *observers = objc_getAssociatedObject(self, JRAssociateArrayKey);
    // 遍历数组
    for (JRObserverInfo *info in observers) {
        if ([info.key isEqualToString:getterName]) {
            // gcd异步调用callback
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
               // info.callback(info.observer, getterName, oldValue, newValue);
               //此处需要处理非object类型转NSValue的操作。
            });
        }
    }
}

回调的时候需要处理非NSObject类型转换为NSValue的操作,由于类型太多,我这里就不展开了,大家感兴趣的可以自己实现一下。后面对各种基础类型的处理还有很多
更多干货文章,可以扫描二维码关注公众号
在这里插入图片描述

你可能感兴趣的:(IOS,Object-C)