美文网首页
iOS 自定义KVO

iOS 自定义KVO

作者: wp_Demo | 来源:发表于2019-01-15 18:44 被阅读0次

通过在了解KVO的实现原理和实现步骤之后,我们可以手动实现KVO,具体可以看最后的demo,这里只讲实现原理

添加观察者大致分为三大步骤
  1. 动态创建一个子类:NSKVONotifying_<obj>
  2. isa指向刚创建的子类
  3. 关联observer

如果要细分的话:

步骤一

1:利用runtime增加新的子类

    NSString *oldName = NSStringFromClass([self class]);
    NSString *newName = [[NSString alloc]initWithFormat:@"NSKVONotifying_%@",oldName];
    //创建并注册类
    Class newClass = NSClassFromString(newName);
    if (!newClass) {
        newClass = objc_allocateClassPair([self class], newName.UTF8String, 0);
        objc_registerClassPair(newClass);
     }

2:在新的类中添加自定义的方法class,setter

        //class
        Method classMethod = class_getInstanceMethod([self class], @selector(class));
        const char* classTypes = method_getTypeEncoding(classMethod);
        class_addMethod(newClass, @selector(class), (IMP)wp_class, classTypes);
        //setter方法
        NSString *setterMethodName = setterForGeter(keyPath);
        SEL setterSEL = NSSelectorFromString(setterMethodName);
        Method setterMethod = class_getInstanceMethod([self class], setterSEL);
        const char* setterTypes = method_getTypeEncoding(setterMethod);
        class_addMethod(newClass, setterSEL, (IMP)wp_setter, setterTypes);

3:返回新的子类

步骤二:

1:使用runtime修改isa指向到步骤一返回的新类

    object_setClass(self, newCLass);
步骤三:

1:使用runtime关联observer

  objc_setAssociatedObject(self, (__bridge void *)@"objc", observer, OBJC_ASSOCIATION_ASSIGN);
实现观察者:

1:在自定义的setter方法中,向父类(需要注意的是:新的子类的父类是Person类)发送setter方法

     objc_msgSendSuper(&superStruct, _cmd, newValue);//此时Person类中的setter方法打印了

2 . 通知观察者,值发生改变了

     id observer = objc_getAssociatedObject(self, (__bridge void *)@"objc");
     NSString* setterName = NSStringFromSelector(_cmd);
     NSString* key = getterForSetter(setterName);
     objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:), key, self, @{key:newValue}, nil);
移除观察者:
  1. 只需要修改isa指针即可:
    Class superClass = class_getSuperclass(object_getClass(self));
    object_setClass(self, superClass);
自动移除观察者:

方法1. 在创建子类时hook系统方法dealloc

    Method m1 = class_getInstanceMethod(object_getClass(self), NSSelectorFromString(@"delloc"));
    Method m2 = class_getInstanceMethod(object_getClass(self), @selector(wp_delloc));
    method_exchangeImplementations(m1, m2);
- (void)wp_delloc{
    [self wp_delloc];
    Class superClass = class_getSuperclass(object_getClass(self));
    object_setClass(self, superClass);
}

方法2. 在创建子类时为新的子类添加dealloc方法

        SEL deallocSEL = NSSelectorFromString(@"dealloc");
        Method deallocMethod = class_getInstanceMethod([self class], deallocSEL);
        const char* deallocTypes = method_getTypeEncoding(deallocMethod);
        class_addMethod(newClass, deallocSEL, (IMP)my_dealloc, deallocTypes);
void my_dealloc(id self,SEL _cmd){
    Class class = object_getClass(self);
    Class superClass = class_getSuperclass(class);
    object_setClass(self, superClass);
}
block实现:

这里实现的比较简单,也是探索吧,在category中新增一个数组保存模型,此模型保存了传过来的observer,keyPath还有block
然后在setter方法中获取数组并且判断keyPath还有block是否存在,执行block,不需要手动告诉观察者值改变了,发送消息.

static void wp_setter(id self,SEL _cmd,id newValue){
    NSLog(@"%s", __func__);
    
    struct objc_super superStruct = {
        self,
        class_getSuperclass(object_getClass(self))
    };
    
    //使用block
    NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd));
    //获取旧值
    id oldValue = objc_msgSendSuper(&superStruct, NSSelectorFromString(keyPath)); //kvc getter方法
    //改变父类的值
    objc_msgSendSuper(&superStruct, _cmd, newValue);//此时使用的Person中的setter方法打印了
    NSMutableArray *array = objc_getAssociatedObject(self, WPKVOKey);
    if (!array) {
        return;
    }
    for (WPInfo *info in array) {
        if ([info.keypath isEqualToString:keyPath] && info.block) {
            info.block(info.observer, keyPath, oldValue, newValue);
            return;
        }
    }
    
}

最后附上Demo地址:https://github.com/gnaw9258wp/KVO_Custom.git

相关文章

网友评论

      本文标题:iOS 自定义KVO

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