美文网首页
手动实现KVO

手动实现KVO

作者: lcus | 来源:发表于2018-07-16 14:25 被阅读10次

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

    相关文章

      网友评论

          本文标题:手动实现KVO

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