自定义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);

}

 

把原来的析构函数注释掉,解决了前面的崩溃问题。

 

原文地址:https://www.cnblogs.com/coolcold/p/12127659.html