刨根问底KVC

最近比较忙,看到关于KVC的文章比较好,本来打算自己写,但是好忙,只能照原作者的搬过来了,但是我自己实现了奥
作者:http://www.cnblogs.com/ioshe/p/5937946.html
—-KVC底层实现,搜索方式, http://ios.jobbole.com/84954/
刨根问底KVC
KVC 全称 key valued coding 键值编码

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性.JAVA,C#都有这个机制。ObjC也有,所以你根部不必进行任何操作就可以进行属性的动态读写,就是KVC。

KVC的操作方法由NSKeyValueCoding提供,而他是NSObject的类别,也就是说ObjC中几乎所有的对象都支持KVC操作。

Person.h

@interface Person : NSObject
{
    int weight;
}
@property(nonatomic,readonly,copy) NSString *name;
@property(nonatomic,readonly, assign) int age;
@property(nonatomic,strong) Dog * dog;
@property(nonatomic,assign) id ID;

-(instancetype)initWithDict:(NSDictionary *)dict;

@end

Person.m

@implementation Person
{
    int _height;
}

-(instancetype)initWithDict:(NSDictionary *)dict
{
    if (self=[super init])
    {
        //字典转模型的常用语句
        [self setValuesForKeysWithDictionary:dict];
    }
    return self;
}

//当key的值是没有定义时,设置会执行的方法
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {

    if ([key isEqualToString:@"id"])
    {
        self.ID = value;
    }
}


//当key的值是没有定义时,取值时执行的方法
- (id)valueForUndefinedKey:(NSString *)key {

    if ([key isEqualToString:@"id"]) {
        return self.ID;
    }
    return [NSNull null];
}

main.m

Person * p1 = [[Person alloc]init];
Dog * d1 = [[Dog alloc] init];
p1.dog = d1;

//setValue:属性值 forKey:属性名(用于简单路径)  
//使用KVC间接修改对象属性时,系统会自动判断对象属性的类型,并完成转换。如该程序中的“170”.      
[p1 setValue:@"170" forKey:@"height"];
[p1 setValue:@"70" forKey:@"weight"];
[p1 setValue:@"1" forKey:@"id"];

//setValue:属性值 forKeyPath:属性路径(用于复合路径)
//用KVC取一个嵌套层次很深的路径的时候,只要给它一个路径就能把想要的属性给拿出来。(.可以理解为路径。一直一直进入)。能够帮助我们很方便的编码。
[p1 setValue:@"dahuan" forKeyPath:@"dog.name"];

//valueForKey:属性名 、valueForKeyPath:属性名(用于复合路径)
NSLog(@"height = %d weight = %d id = %@ dog.name = %@" ,[[p1 valueForKey:@"height"] intValue],[[p1 valueForKey:@"weight"] intValue],[p1 valueForKey:@"id"],[p1 valueForKeyPath:@"dog.name"]);

log日志:height = 170 weight = 70 id = 11111 dog.name = dahuan
所有的属性都可以赋值成功


NSDictionary * dict = @{@"height":@"160",
                        @"weight":@"60",
                        @"id":@"11101"
                        };

Person * p2 = [[Person alloc] initWithDict:dict];

NSLog(@"height = %d weight = %d id = %@",[[p2 valueForKey:@"height"] intValue],[[p2 valueForKey:@"weight"] intValue],[p2 valueForKey:@"id"]);

log日志:height = 160 weight = 60 id = 11101
所有的属性都可以赋值成功

KVC查找属性的顺序

用@property定义的属性的key值
setter方法的key值
直接访问成员变量,先找key,如果找不到,再找_key
以上三种都未找到就会调用- (void)setValue:(id)value forUndefinedKey:(NSString *)key 方法。
如果没有重写setValue:forUndefinedKey程序会马上崩溃。
注意:KVC可以访问成员变量,无论是否提供getter/setter方法,无论可见性是怎样,是否有readonly修饰。

setValue:forUndefinedKey与valueForUndefinedKey的应用

KVC的主要用途无非是ORM映射,就是将dictionary转换成model,但有些服务器返回的字段有可能是oc的关键字比如‘id’,’description’等。如上代码举得id的例子,我们无法让@property后面key值为id,于是使用大写的ID代替,KVC是区分大小写的我们不用担心。这时我们只需在setValue:forUndefinedKey把id的key值赋值给ID的key值,就可以避免关键字的尴尬。

KVC的逆向使用

Person * p1 = [[Person alloc]init];
[p1 setValue:@"170" forKey:@"height"];
[p1 setValue:@"70" forKey:@"weight"];
[p1 setValue:@"11111" forKey:@"id"];


NSArray * arr = @[@"height",@"weight",@"id"];
NSDictionary * dict = [p1 dictionaryWithValuesForKeys:arr];
NSLog(@"%@",dict);

log日志:
{
height = 170;
id = 11111;
weight = 70;
}
NSArray/NSSet等都支持KVC

Person * p1 = [[Person alloc]init];
Dog * d1 = [[Dog alloc] init];
d1.name = @"iPhone";
p1.dog = d1;

Person * p2 = [[Person alloc]init];
Dog * d2 = [[Dog alloc] init];
d2.name = @"ios";
p2.dog = d2;

NSArray *persons=@[p1,p2];

NSArray *arrayM=[persons valueForKeyPath:@"dog.name"];
NSLog(@"%@",arrayM);

log日志:
(
iPhone,
ios
)
KVC计算

Person * person = [[Person alloc] init];

NSMutableArray * books = [NSMutableArray array];
for (int i = 0; i < 10; i++) {
    Book * book = [[Book alloc] init];
    book.price = i;
    [books addObject:book];
}

person.books = books;

NSNumber *bookCount = [person valueForKeyPath:@"books.@count"];
NSLog(@"book count :%@", bookCount);

NSNumber *sum = [person valueForKeyPath:@"books.@sum.price"];
NSLog(@"sum :%@", sum);

NSNumber *avg = [person valueForKeyPath:@"books.@avg.price"];
NSLog(@"vag :%@", avg);

NSNumber *min = [person valueForKeyPath:@"books.@min.price"];
NSLog(@"min :%@", min);

NSNumber *max = [person valueForKeyPath:@"books.@max.price"];
NSLog(@"max :%@", max);
KVC底层实现

比如说如下的一行KVC的代码:

[person setValue:@”dahuan” forKey:@”name”];

就会被编译器处理成:

SEL sel = sel_get_uid (“setValue:forKey:”);
IMP method = objc_msg_lookup (person->isa,sel);
method(person, sel, @”dahuan”, @”name”);

KVC与runtime应用

#import "MyModel.h"
#import <objc/runtime.h>

@implementation MyModel

//解档
- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        //获取类中所有成员变量名
        Ivar *ivar = class_copyIvarList([MyModel class], &count);
        for (int i = 0; i<count; i++) {
            Ivar iva = ivar[i];
            const char *name = ivar_getName(iva);
            NSString *strName = [NSString stringWithUTF8String:name];
            //进行解档取值
            id value = [decoder decodeObjectForKey:strName];
            //利用KVC对属性赋值
            [self setValue:value forKey:strName];
        }
        free(ivar);
    }
    return self;
}

//归档
- (void)encodeWithCoder:(NSCoder *)encoder
{
    unsigned int count;
    Ivar *ivar = class_copyIvarList([MyModel class], &count);
    for (int i=0; i<count; i++) {
        Ivar iv = ivar[i];
        const char *name = ivar_getName(iv);
        NSString *strName = [NSString stringWithUTF8String:name];
        //利用KVC取值
        id value = [self valueForKey:strName];
        [encoder encodeObject:value forKey:strName];
    }
    free(ivar);
}
@end

最后附苹果KVC官方文档:

https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/KeyValueCoding.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值