自定义KVO(三)
下面来实现KVO自动销毁:(在合适的时候,自动移除观察者)
@implementation NSObject (FXKVO)
- (void)dealloc{
//指回父类
Class superClass = [self class];//KVOStudent
object_setClass(self, superClass);
}
这里出现一个问题,就是VC在销毁时候dealloc方法不调用:
原因是dealloc会被所有NSObject的对象调用,相当于修改了系统方法,所以这样写是不对的
使用运行时替换方法
- (void)fx_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(FXKeyValueObservingOptions)options context:(nullable void *)context handBlock:(FXKVOBlock)handBlock{
//1. 验证set方法
[self judgeSetterMethodFromKeyPath:keyPath];
//2. 动态生成子类
Class newClass = [self creatChildClass:keyPath];
//3. 当前对象的类,isa指向newClass
object_setClass(self, newClass);
//4. 保存KVO信息
//集合 --> add map
FXKVOInfo *info = [[FXKVOInfo alloc] initWithObserver:observer forKeyPath:keyPath options:options handBlock:handBlock];
NSMutableArray *infoArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(FXKVOAssiociateKey));
if(!infoArray) {
//数组 -> 成员 强引用
//self(vc) -> person ISA -> 数组 -> info -/weak/-> self(VC) ?
//self.person -> FXKVO -> //内存问题,这里为什么没有形成循环引用?
infoArray = [NSMutableArray arrayWithCapacity:1];
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(FXKVOAssiociateKey), infoArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[infoArray addObject:info];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method m1 = class_getInstanceMethod([self class], NSSelectorFromString(@"dealloc"));
Method m2 = class_getInstanceMethod([self class], @selector(fx_dealloc));
method_exchangeImplementations(m1, m2);
});
}
发现dealloc野指针崩溃,拿不到父类,原因是在交互dealloc函数的时候,
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method m1 = class_getInstanceMethod([self class], NSSelectorFromString(@"dealloc"));
Method m2 = class_getInstanceMethod([self class], @selector(fx_dealloc));
method_exchangeImplementations(m1, m2);
});
[self class]父类(KVOPerson)里面是没有析构函数(dealloc)的,所以这里交换的还是NSObject里面的析构函数,和之前的问题一样,交互了系统类,出现了不可预见的崩溃。
那么目前的想法就是给父类添加一个dealloc方法是否就可以了,这样的话是否每个子类都要手动添加dealloc方法,不是很方便,而且不符合KVO原来的设计思想,KVO的设计本来的目的是想不对原来的类做侵入,所以和最初的设计思想是相悖的。
所以初步想法是在动态子类里面添加析构方法(dealloc),在动态子类的析构函数里面,释放数组和info,这也是为什么系统给KVO添加dealloc,对原类没有侵入性,非常的安全。下面尝试下:
// 添加dealloc -- 为什么系统给KVO添加dealloc
SEL deallocSEL = NSSelectorFromString(@"dealloc");
Method deallocM = class_getInstanceMethod([self class], deallocSEL);
const char *deallocType = method_getTypeEncoding(deallocM);
class_addMethod(newClass, setterSEL, (IMP)fx_dealloc, deallocType);
static void fx_dealloc(id self, SEL _cmd, id newValue){
Class superClass = [self class];//KVOStudent
object_setClass(self, superClass);
}
把原来的析构函数注释掉,解决了前面的崩溃问题。