手动实现KVO

1 首先根据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 检测observer 检测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 根绝当前判断有没有生成过带前缀的子类对象 如果没有生成子类对象 注册子类

    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 重写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;
    
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。