最近项目开发用到了派生子类的知识,从网上看了一下手动实现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的操作,由于类型太多,我这里就不展开了,大家感兴趣的可以自己实现一下。后面对各种基础类型的处理还有很多
更多干货文章,可以扫描二维码关注公众号