手動(dòng)實(shí)現(xiàn)KVO

1 首先根據(jù)key 生成Set方法

SEL setter = NSSelectorFromString(setterForKeyPath(keyPath));
static NSString* setterForKeyPath(NSString*keyPath){
    
    if (!keyPath.length) return nil;
    
    NSString *firstLetter = [keyPath substringToIndex:1].uppercaseString;
    
    NSString *secondLetter =[keyPath substringFromIndex:1];
    
    return [NSString stringWithFormat:@"set%@%@",firstLetter,secondLetter];
}

2 檢測(cè)observer 檢測(cè)set方法 是否存在 不存在拋出異常

 Method setterMethos = class_getInstanceMethod([observer class], setter);
    
    if (!setterMethos) {
        
        NSString *reason =[NSString stringWithFormat:@"Object %@ does not have a setter for key%@",observer,keyPath];
        @throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil];
        
        return;
        
    }

3 根絕當(dāng)前判斷有沒(méi)有生成過(guò)帶前綴的子類(lèi)對(duì)象 如果沒(méi)有生成子類(lèi)對(duì)象 注冊(cè)子類(lèi)

    Class clazz = object_getClass(self);
    
    NSString *className = NSStringFromClass(clazz);
    
    if (![className hasPrefix:@"LLKVOPreFix"]) {
        
        object_setClass(self, [self makeKvoClassWithOriginalClassName:className]);
        
    }

-(Class)makeKvoClassWithOriginalClassName:(NSString*)originalClazzName{
    
    NSString *kvoClassName =[@"LLKVOPreFix" stringByAppendingString:originalClazzName];
    
    Class class = NSClassFromString(kvoClassName);
    
    if (!class) {
        return class;
    }
    Class orginalClazz = object_getClass(self);
    Class kvoClazz = objc_allocateClassPair(orginalClazz, kvoClassName.UTF8String, 0);
    
    Method classMethod = class_getInstanceMethod(orginalClazz, @selector(class));
    
    const char *type = method_getTypeEncoding(classMethod);
    
    class_addMethod(kvoClazz, @selector(class), (IMP)kvo_class, type);
    
    objc_registerClassPair(kvoClazz);
    
    return kvoClazz;
}
static Class kvo_class(id self, SEL _cmd)
{
    return class_getSuperclass(object_getClass(self));
}


5 重寫(xiě)set方法

   if (![self hasSelector:setter]) {
        
        const char *types = method_getTypeEncoding(setterMethos);
        
        class_addMethod(class, setter, (IMP)kvo_setter, types);
    }

static void kvo_setter(id self,SEL _cmd,id newValue){
    
    NSString *setterName = NSStringFromSelector(_cmd);
    
    NSString *getterName = getterForSetterName(setterName);
    
    
    id oldValue = [self valueForKey:getterName];
    
    struct objc_super superclass = {
        
        .receiver = self,
        .super_class = class_getSuperclass(object_getClass(self))
    };
    
    void(*objc_msgSendSuperCaster)(void*,SEL,id) = (void *)objc_msgSendSuper;
    
    
    objc_msgSendSuperCaster(&superclass,_cmd,newValue);
    
    
}
static NSString *getterForSetterName(NSString * setterName){
    
    if (setterName.length<=0||![setterName hasPrefix:@"set"]||![setterName hasSuffix:@":"]) {
        return nil;
    }
    
    NSRange range = NSMakeRange(3, setterName.length-4);
    
    NSString *key = [setterName substringWithRange:range];

    NSString *fitst =[key substringToIndex:1].lowercaseString;
    
    key =[key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:fitst];
    
    return key;
    
}
-(BOOL)hasSelector:(SEL)selctor{
    
    Class clazz = object_getClass(self);
    
    unsigned int count  = 0;
    
    Method *methodList = class_copyMethodList(clazz, &count);
    
    for (int i= 0; i<count; i++) {
        
        SEL thisSelector =method_getName(methodList[i]);
       
        if (thisSelector == selctor) {
            free(methodList);
            
            return YES;
        }

    }
    
    free(methodList);
    
    return NO;
    
}

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

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