iOS设计模式 —— KVC

刨根问底KVC

KVC 全称 key valued coding 键值编码

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

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

Person.h

123456789101112
@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

1234567891011121314151617181920212223242526272829303132333435

@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

1234567891011121314151617181920212223242526272829303132
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查找属性的顺序

  1. 用@property定义的属性的key值
  2. setter方法的key值
  3. 直接访问成员变量,先找key,如果找不到,再找_key
  4. 以上三种都未找到就会调用- (void)setValue:(id)value forUndefinedKey:(NSString *)key 方法。
  5. 如果没有重写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的逆向使用

12345678910111213141516
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

1234567891011121314151617181920
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 *[email protected][p1,p2];

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

log日志:(    iPhone,    ios)

KVC计算

12345678910111213141516171819202122232425
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:@"[email protected]"];NSLog(@"book count :%@", bookCount);

NSNumber *sum = [person valueForKeyPath:@"[email protected]"];NSLog(@"sum :%@", sum);

NSNumber *avg = [person valueForKeyPath:@"[email protected]"];NSLog(@"vag :%@", avg);

NSNumber *min = [person valueForKeyPath:@"[email protected]"];NSLog(@"min :%@", min);

NSNumber *max = [person valueForKeyPath:@"[email protected]"];NSLog(@"max :%@", max);

KVC底层实现

123456789
比如说如下的一行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应用

123456789101112131415161718192021222324252627282930313233343536373839404142
#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

另外.....

我的愿望是.......

世界和平.........

时间: 2024-10-05 10:20:25

iOS设计模式 —— KVC的相关文章

iOS 中KVC、KVO、NSNotification、delegate 总结及区别

iOS 中KVC.KVO.NSNotification.delegate 总结及区别 1.KVC,即是指 NSKeyValueCoding,一个非正式的Protocol,提供一种机制来间接访问对象的属性.而不是通过调用Setter.Getter方法访问.KVO 就是基于 KVC 实现的关键技术之一. Demo: @interface myPerson : NSObject { NSString*_name; int      _age; int      _height; int      _w

IOS设计模式之四(备忘录模式,命令模式)

本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq)翻译,如果你发现有什么错误,请与我联系谢谢. 备忘录(Memento)模式 备忘录模式快照对象的内部状态并将其保存到外部.换句话说,它将状态保存到某处,过会你可以不破坏封装的情况下恢复对象的状态,也就是说原来对象中的私有数据仍然是私有的. 如何使用备忘录模式 在ViewController.m中增加

IOS设计模式之二(门面模式,装饰器模式)

本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq)翻译,如果你发现有什么错误,请与我联系谢谢. 门面(Facade)模式(译者注:facade有些书籍译为门面,有些书籍译为外观,此处译为门面) 门面模式针对复杂的子系统提供了单一的接口,不需要暴漏一些列的类和API给用户,你仅仅暴漏一个简单统一的API. 下面的图解释了这个概念: 这个API的使用者

IOS设计模式之三(适配器模式,观察者模式)

本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq)翻译,如果你发现有什么错误,请与我联系谢谢. 适配器(Adapter)模式 适配器可以让一些接口不兼容的类一起工作.它包装一个对象然后暴漏一个标准的交互接口. 如果你熟悉适配器设计模式,苹果通过一个稍微不同的方式来实现它-苹果使用了协议的方式来实现.你可能已经熟悉UITableViewDelegat

IOS设计模式之一(MVC模式,单例模式)

本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq)翻译,如果你发现有什么翻译错误,请与我联系谢谢. iOS 设计模式-你可能已经听说过这个词,但是你真正理解它意味着什么吗?虽然大多数的开发者可能都会认为设计模式是非常重要的,然而关于设计模式这一主题的文章却不多,并且有时候我们开发者在写代码的时候也不会太关注它. 在软件设计领域,设计模式是对通用问题

iOS设计模式(代码分析系列2:简单工厂模式)

简单工厂模式示例代码下载地址, 1.简述 首先需要说明一下,简单工厂模式不属于23种GOF设计模式之一.它也称作静态工作方法模式,是工厂方法模式的特殊实现(也就是说工厂模式包含简单工厂模式).这里对简单工厂模式进行介绍,是为后面的工厂方法和抽象工厂模式做一个引子. 2.定义 "专门定义一个类来负责创建其他类的实例,被创建的实例通常具有共同的父类." 世界上就是由一个工厂类,根据传入的参数,动态地决定创建出哪一个产品类的实例. 3.结构图 简要分析结构图: ConcreteProduct

IOS设计模式之三:MVC模式

IOS设计模式之三:MVC模式 提到ios中的mvc不得不提2011秋季斯坦福课程的老头,他的iphone开发公开课是所有描述ios中mvc模式最为准确并且最为浅显易懂的. 模型-视图-控制器 这个模式其实应该叫做MCV,用控制器把model与view隔开才对,也就是model与view互相不知道对方的存在,没有任何瓜葛,他们就像一个团队里吵了架的同事,如果有项目需要他俩来参与,那么最好有第三者来管理他俩之间的沟通与协调.这个第三者就是控制器. 既然管理,那么姑且就把这个控制器提做项目经理吧,这

iOS设计模式 - (3)简单工厂模式

iOS设计模式 - (3)简单工厂模式           by Colin丶 转载请注明出处:              http://blog.csdn.net/hitwhylz/article/details/40381721 一.简述 简单工厂模式(FACTORY),通过面向对象的封装,继承和多态来降低程序的耦合度.将一个具体类的实例化交给一个静态工厂方法来执行. 该模式中的角色包括: 工厂类(Simple Factory): 只包含了创建具体类的静态方法. 抽象产品(Product):

iOS设计模式

iOS设计模式,很多开发这都是听得多,但是有时候自己即使用过也不会很在意,开发者在写代码的时候也不会注意它. 在软件设计领域,设计模式是对通过问题的可复用的解决方案.设计模式是一系列帮你写出可理解和复用的模板,设计模式帮你创建松耦合的代码,你不需要花费太多就可以改变或者替换代码中的组件. (1)代理模式 应用场景:当一个类的某些功能需要由其他别的类别来实现的,但是又不确定是哪个类 优势:松耦合 实例:tableView的数据源delegate,通过和protocol的配合,完成委托. 列表row