iOS 自定義KVO

通過在了解KVO的實現(xiàn)原理和實現(xiàn)步驟之后,我們可以手動實現(xiàn)KVO,具體可以看最后的demo,這里只講實現(xiàn)原理

添加觀察者大致分為三大步驟
  1. 動態(tài)創(chuàng)建一個子類:NSKVONotifying_<obj>
  2. isa指向剛創(chuàng)建的子類
  3. 關(guān)聯(lián)observer

如果要細分的話:

步驟一

1:利用runtime增加新的子類

    NSString *oldName = NSStringFromClass([self class]);
    NSString *newName = [[NSString alloc]initWithFormat:@"NSKVONotifying_%@",oldName];
    //創(chuàng)建并注冊類
    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關(guān)聯(lián)observer

  objc_setAssociatedObject(self, (__bridge void *)@"objc", observer, OBJC_ASSOCIATION_ASSIGN);
實現(xiàn)觀察者:

1:在自定義的setter方法中,向父類(需要注意的是:新的子類的父類是Person類)發(fā)送setter方法

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

2 . 通知觀察者,值發(fā)生改變了

     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. 在創(chuàng)建子類時hook系統(tǒng)方法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. 在創(chuàng)建子類時為新的子類添加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實現(xiàn):

這里實現(xiàn)的比較簡單,也是探索吧,在category中新增一個數(shù)組保存模型,此模型保存了傳過來的observer,keyPath還有block
然后在setter方法中獲取數(shù)組并且判斷keyPath還有block是否存在,執(zhí)行block,不需要手動告訴觀察者值改變了,發(fā)送消息.

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容