美文网首页
KVC 总结

KVC 总结

作者: Lucky_Roc | 来源:发表于2019-01-16 13:49 被阅读0次

    认识 KVC

    KVC (Key-Value Coding), 它是一种用间接方式访问类的属性的机制。在 Swift 中为一个类实现 KVC 的话,需要让它继承自 NSObject:

    class Person: NSObject {
        var firstName: String
        var lastName: String
        init(firstName: String, lastName: String) {
            self.firstName = firstName
            self.lastName = lastName
        }
    }
    

    这样,我们就可以使用 KVC 的方式访问 Person 类的属性了:

    let p = Person(firstName: "Lucky", lastName: "Roc")
    //使用 KVC 机制赋值的方式
    p.setValue("swift", forKey: "firstName")
    // 使用直接引用属性的方式
    print(p.lastName)
    // 使用 KVC 机制访问的方式
    print(p.valueForKey("lastName")!)
    

    KVC 有什么用?

    可以不用过多的依赖编译时的限制,为我们提供了更多的运行时的能力,免去我们调用gettersetter方法,从而简化我们的代码,也可以用来修改系统控件内部属性(这个黑魔法且用且珍惜)。

    KVC的运行机制

    setValue: forKey

    let p = Person(firstName: "Lucky", lastName: "Roc")
    //使用 KVC 机制赋值的方式
    p.setValue("swift", forKey: "firstName")
    
    image

    总结: 如果没有找到Set<Key>方法的话,会按照_key_iskeykeyiskey的顺序搜索成员并进行赋值操作

    valueForKey

    let p = Person(firstName: "Lucky", lastName: "Roc")
    print(p.valueForKey("lastName")!)
    
    image

    valueForKeyPath

    在开发过程中,一个类的成员变量有可能是其他的自定义类,你可以先用KVC获取出来该属性,然后再次用KVC来获取这个自定义类的属性,但这样是比较繁琐的,对此,KVC提供了一个解决方案,那就是键路径KeyPath

    class Address: NSObject {
        var firstLine: String
        var secondLine: String
        init(firstLine: String, secondLine: String) {
            self.firstLine = firstLine
            self.secondLine = secondLine
        }
    }
    
    class PersonHandleKeyPath: NSObject {
        var firstName: String
        var lastName: String
        var address: Address
        init(firstName: String, lastName: String, address: Address) {
            self.firstName = firstName
            self.lastName = lastName
            self.address = address
        }
    }
    
    var p = PersonHandleKeyPath(firstName: "Lucky", lastName: "Roc", address: Address(firstLine: "Nanjing", secondLine: "ZhujiangRoad"))
    print(p.valueForKeyPath("address.firstLine")!)
    

    KVC的异常处理

    p.setValue(nil, forKey: "firstName")
    p.valueForKey("age")!
    

    以上的2个方法都会导致Crash,为了避免Crash我们通常会重写如下的2个方法

    //如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常
    - (nullable id)valueForUndefinedKey:(NSString *)key;
    
    //如果你在SetValue方法时给Value传nil,则会调用这个方法
    - (void)setNilValueForKey:(NSString *)key;
    

    KVC和Runtime的关系

    [dic setVaule:@"zhangsan" forKey:@"name"];
    

    当运行的时候就会被编译成

    // 根据方法名找到运行方法的时候所需要的环境参数
    SEL sel = sel_get_uid("setValue:forKey:");
    // 从自己isa指针结合环境参数,找到具体的方法实现接口
    IMP method = objc_msg_lookup(dic->isa,sel);
    method(dic,sel,@"zhangsan",@"name");
    

    KVC使用场景

    单层字典模型转化(字典转model)

    [self.loginModel setValuesForKeysWithDictionary:resultDic];
    

    (iOS黑魔法) 通过RunTime获取控件API未暴露的属性,自定义修改

    例:通过KVC拿到UITextField的占位label,修改颜色
    UILabel *placeholderLabel=[self.userTextField valueForKeyPath:@"placeholderLabel"];
    placeholderLabel.textColor = [UIColor redColor];
    

    使用valueForKeyPath可以获取数组中的最小值、最大值、平均值、求和

    CGFloat sum = [[array valueForKeyPath:@"@sum.floatValue"] floatValue];
    CGFloat avg = [[array valueForKeyPath:@"@avg.floatValue"] floatValue];
    CGFloat max =[[array valueForKeyPath:@"@max.floatValue"] floatValue];
    CGFloat min =[[array valueForKeyPath:@"@min.floatValue"] floatValue];
    

    KVC相关面试题

    在KVC 中如何保持程序的健壮性?

    重写对象的valueforkey和setvalueforkey方法。

    KVC 中的隐藏方法有什么?

    max,min,count,sum 等

    相关文章

      网友评论

          本文标题:KVC 总结

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