美文网首页
KVO原理解析--自定义KVO

KVO原理解析--自定义KVO

作者: rookiesss | 来源:发表于2018-12-06 10:50 被阅读0次

    kvo的用法就不再赘述。

    [self.name addObserver:self
                     forKeyPath:@"name"
                        options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew
                        context:nil];
    
    -(void)observeValueForKeyPath:(NSString *)keyPath
                         ofObject:(id)object
                           change:(NSDictionary<NSString *,id> *)change
                          context:(void *)context{
            NSLog(@"%@", change);
    }
    

    核心思想,以观察name属性为例:
    重写name属性的setter方法,当name值改变时在setter方法中调用observeValueForKeyPath方法。

    先说说看我自己的思路:
    1.替换name属性的setter方法。

    - (void)dy_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath 
                   options:(NSKeyValueObservingOptions)options context:(nullable void *)context {
    
        NSString* setterMethodName = setterForGetter(keyPath);
        SEL setterSEL = NSSelectorFromString(setterMethodName);
        Method setterMethod = class_getInstanceMethod([self class], setterSEL);
        const char* setterTypes = method_getTypeEncoding(setterMethod);
      
        //替换name的setter方法自己实现
        class_replaceMethod([self class], setterSEL, (IMP)dy_setter, setterTypes);
        //将观察者存到数组作为属性,方便后面使用
        NSMutableArray *observerArr = objc_getAssociatedObject(self, (__bridge void *)@"observerArr")?: [NSMutableArray array];
        
        if (![observerArr containsObject:observer]) [observerArr insertObject:observer atIndex:0];
        objc_setAssociatedObject(self, (__bridge void *)@"observerArr", observerArr, OBJC_ASSOCIATION_RETAIN);
    }
    

    2.在name改变时通过重写name属性的setter方法调用observeValueForKeyPath方法。

    static void dy_setter(id self, SEL _cmd, id newValue) {
    
        NSString* setterName = NSStringFromSelector(_cmd);
        NSString* key = getterForSetter(setterName);
        char *char_content = (char *)[[NSString stringWithFormat:@"_%@",key] cStringUsingEncoding:NSASCIIStringEncoding];
        Ivar ivar = class_getInstanceVariable([self class], char_content);
        //给name属性赋值
        object_setIvar(self, ivar, newValue);
        // 通知观察者, 值发生改变了
        id observerArr = objc_getAssociatedObject(self, (__bridge void *)@"observerArr");
        for (id observer in observerArr) {
            [observer observeValueForKeyPath:key ofObject:self change:@{key:newValue} context:nil];
        }
    }
    

    3.setterForGetter & getterForSetter。

    #pragma mark - 从get方法获取set方法的名称 key ===>>> setKey:
    static NSString  * setterForGetter(NSString *getter){
        
        if (getter.length <= 0) { return nil; }
        
        NSString *firstString = [[getter substringToIndex:1] uppercaseString];
        NSString *leaveString = [getter substringFromIndex:1];
        
        return [NSString stringWithFormat:@"set%@%@:",firstString,leaveString];
    }
    
    #pragma mark - 从set方法获取getter方法的名称 set<Key>:===> Key
    static NSString * getterForSetter(NSString *setter){
        
        if (setter.length <= 0 || ![setter hasPrefix:@"set"] || ![setter hasSuffix:@":"]) { return nil;}
        
        NSRange range = NSMakeRange(3, setter.length-4);
        NSString *getter = [setter substringWithRange:range];
        NSString *firstString = [[getter substringToIndex:1] lowercaseString];
        getter = [getter stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:firstString];
        
        return getter;
    }
    
    

    4.移除观察者.

    如果Person中重写了setName方法这样做会导致Peroson中setName无效,在苹果的实现中,先创建了一个self的子类,在子类中重写 了setName方法,这样能保证Peroson中重写的setName有效。
    代码类似,代码在这里

    相关文章

      网友评论

          本文标题:KVO原理解析--自定义KVO

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