Objective-C之KVC(键值编码)

IOS中Key-Value Coding (KVC)俗称键值编码,是一个非正式的协议,它提供一种机制来间接访问对象的属性。
直接访问对象是通过调用访问器的方法实现,而kvc不需要调用访问器的设置和获取方法,可以直接访问对象的属性。   
KVC的操作方法由NSKeyValueCoding协议提供,而NSObject就实现了这个协议,也就是说ObjC中几乎所有的对象都支持KVC操作,
常用的KVC操作方法如下:
1)对属性赋值(动态设置): 
setValue:属性值 frKey:属性名(用于简单路径);
setValue:属性值 forKeyPath:属性路径(用于复合路径,例如Person有一个Account类型的属性,那么person.account就是一个复合属性);
kvc的一般用法:

setValue:属性值 forKey:属性名和valueForKey:属性名

- (void)viewDidLoad {
    [super viewDidLoad];
    [self test1];
}
#pragma mark --setValue:属性值 forKey:属性名
-(void)test1{
    Person * person = [[Person alloc]init];
    [person setValue:@"一个屌丝的程序员" forKey:@"name"];
    NSString * name =[person valueForKeyPath:@"name"];
    NSLog(@"%@",name);
}

输出结果:


2)对属性取值(动态读取): 
valueForKey:属性名 (用于简单路径);
valueForKeyPath:属性名(用于复合路径);

setValue:属性值 forKeyPath:属性路径和valueForKeyPath:属性名(用于复合路径)

- (void)viewDidLoad {
    [super viewDidLoad];
    [self test2];
}
#pragma mark --setValue:属性值 forKeyPath:属性路径和valueForKeyPath:属性名(用于复合路径)
-(void)test2{
    Person * person = [[Person alloc]init];
    person.course = [[Course alloc]init];
    [person setValue:@"Course是一个自定义类,course是student的属性" forKeyPath:@"course.CourseName"];
     NSString * CourseName = [person valueForKeyPath:@"course.CourseName"];
     NSLog(@"CourseName:%@",CourseName);
}
输出结果:


注意
(1). key的值必须正确,如果拼写错误,会出现异常

          1> * 首先去接收者(调用方法的那个对象)的类中查找与key相匹配的访问器方法(-set<Key>),如果找到了一个方法,就检查它参数的类型,如果它的参数类型不是一个对象指针类型,但是只为nil,就会执行setNilValueForKey:方法,setNilValueForKey:方法的默认实现,是产生一个NSInvalidArgumentException异常,但是你可以重写这个方法.

               * 如果方法参数的类是一个对象指针类型,就会简单的执行这个方法,传入对应的参数.如果方法的参数类型是NSNumberNSValue的对应的基本类型,先把它转换为基本数据类,再执行方法,传入转换后的数据.

          2> * 如果没有对应的访问器方法(setter方法),如果接受者的类的+accessInstanceVariablesDirectly方法返回YES,那么就查找这个接受者的与key相匹配的实例变量(匹配模式为_<key>,_is<Key>,<key>,is<Key>):比如:key为age,只要属性存在_age,_isAge,age,isAge中的其中一个就认为匹配上了,如果找到这样的一个实例变量,并且的类型是一个对象指针类型,

          3> * 首先released对象上的旧值,然后把传入的新值retain后的传入的值赋值该成员变量,如果方法的参数类型是NSNumber或NSValue的对应的基本类型,先把它转换为基本数据类,再执行方法,传入转换后的数据.

               * 如果访问器方法和实例变量都没有找到,执行setValue:forUndefinedKey:方法,该方法的默认实现是产生一个NSUndefinedKeyException 类型的异常,但是我们可以重写setValue:forUndefinedKey:方法

(2). 当key的值是没有定义的,valueForUndefinedKey:这个方法会被调用,如果你自己写了这个方法,key的值出错就会调用到这里来
(3). 因为类key反复嵌套,所以有个keyPath的概念,keyPath就是用.号来把一个一个key链接起来,这样就可以根据这个路径访问下去
(4). NSArrayNSSet等都支持KVC

3)key值找不到时,异常处理

使用kvc时,如果代码中的key值不存在,会抛出异常,可以在类中通过重写它提供

- (void)setValue:(id)value forUndefinedKey:(NSString *)key</span>
当key不存在时,会自动调用上面的这个方法,可以在这个方法中进行处理

- (void)setValue:(id)value forUndefinedKey:(NSString *)key  {
    if([key isEqualToString:@"id"])
        self.userid = value;
}

4)KVC的字典转模型和模型转字典

1>字典转模型

当我们需要把json字符串反序列化成我们想要的对象 ,可以通过下面的方法

- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues

- (id)initWithDictionary:(NSDictionary *)dictionary {
    self = [self init];
    if (self){
        [self setValuesForKeysWithDictionary:dictionary];
    }
    return self;
}

注意:字典中的key 和对象属性要求一样

2>模型转字典

- (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys;

 NSDictionary *dic = [self.dataArray dictionaryWithValuesForKeys:@[@"name",@"age"]];
 for (int i =0 ; i<dic.count; i++) {    
      NSLog(@"%@", [dic objectForKey:[dic allKeys][i]]);
}
5)总结

1)KVC需要把字符串属性名称转换后才能赋值,调用的set方法,性能消耗较高

2)KVC查找一个属性进行读取的一般顺序

1>动态设置属性,
    优先考虑调用setA方法,如果没有该方法则优先考虑搜索成员变量_a,如果仍然不存在则搜索成员变量a,如果最后仍然没搜索到则会调用这个类的setValue:forUndefinedKey:方法(注意搜索过程中不管这些方法、成员变量是私有的还是公共的都能正确设置);
2>动态读取属性
    优先考虑调用a方法(属性a的getter方法),如果没有搜索到则会优先搜索成员变量_a,如果仍然不存在则搜索成员变量a,如果最后仍然没搜索到则会调用这个类的valueforUndefinedKey:方法(注意搜索过程中不管这些方法、成员变量是私有的还是公共的都能正确读取);

3>解析的数据 类型不确定处理

 -(void)setValue:(id)value forKey:(NSString *)key{
      if ([key isEqualToString:@"itemCode"]) {
           self.itemCode =[NSString stringWithFormat:@"%@",value];
      }else{
          [super setValue:value forKey:key];
     }
 }

别忘加上这个,也要重写(未被定义的不做处理):

-(void)setValue:(id)value forUndefinedKey:(NSString *)key{ }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值