引言: IOS为我们提供了KVC,KVO的实现机制,但在一般的APP开发中,应用这种技术的比较少,但是这种技术在多数据型项目处理中可以起到很好的简化代码、APP整体架构的作用,本篇中只介绍KVC的在开发项目中的应用,以及相关发散性技术。
简单介绍一下KVC: Key-Value Coding,它是一种使用字符串标识符,间接访问对象属性的机制,在某种程度上跟map的关系匪浅,更深入点使用反射机制对类成员变量进行操作。
先看使用方法:
1、先定义一个类。
1 /*! 2 @Description RSS源分类目录实体 3 @see Entity.h 4 @author sherwin.chen 5 @version 1.0.0 6 */ 7 @interface RSSCategoryEntity : Entity 8 @property (nonatomic, copy) NSString *strName; //分类名 9 @property (nonatomic, copy) NSString *strIconName; //分类icon图标 10 @property (nonatomic, assign) NSInteger nRssNum; //rss条数目
11 @end
2.外部使用RSSCategoryEntity实体类
1 RSSCategoryEntity *rss = [[RSSCategoryEntity alloc] init]; 2 3 //key use 4 [rss setValue:@"sherwin.chen" forKey:@"strName"]; 5 [rss setValue:@"iRSS.icon" forKey:@"strIconName"]; 6 7 NSString* strRSSName = [rss valueForKey:@"strName"]; 8 NSString* strIconName= [rss valueForKey:@"strIconName"]; 9 10 //normal use 11 strRSSName = rss.strName; 12 strIconName= rss.strIconName;
显然,在一般的项目中,使用KVC操作会使代码冗长,故这种技术在IOS开发中一般很少使用,但是好的技术得用在好的地方。通常,在我们与服务端API接口进行数据交换中,在得到服务端返回的模型化对象 JSON串中,常用的处理就是使用 objectForKey: 来初使化自定义模块。
1 NewsEntity *newsEnt = self; 2 newsEnt.strId = [dict objectForKey:BASE_FILDS_ID]; 3 newsEnt.strTitle = [dict objectForKey:NEWS_FILDS_Title]; 4 newsEnt.arImageUrl = [dict objectForKey:NEWS_FILDS_ImageUrl]; 5 newsEnt.strCategoryID = [dict objectForKey:NEWS_FILDS_CategoryID];
但有了 KVC,我们可以优雅的将JSON串转换为模型对象。
假设服务器返回的JOSN串为:
1 { 2 "id":"20140308", 3 "name":"IOS Dev", 4 "iconName":"icon.png" 5 "date":1394254208 6 }
为此,我们可以设计此JSON对应有模型:
1 @interface RSSEntity:Entity 2 @property (nonatomic, copy) NSString *itemID; 3 @property (nonatomic, copy) NSString *name; 4 @property (nonatomic, copy) NSString *iconName; 5 @property (nonatomic, assign) int unixDate; 6 7 //模型初使化接口 8 -(id) initWithDictionary:(NSDictionary*) jsonDict; 9 10 @end
1 @implementation RSSEntity 2 -(id)initWithDictionary:(NSDictionary *)jsonDict 3 { 4 if (self=[super init]) { 5 [self init]; 6 [self setValuesForKeysWithDictionary:jsonDict]; 7 } 8 return self; 9 } 10 11 -(void)setValue:(id)value forUndefinedKey:(NSString *)key 12 { 13 if ([key isEqualToString:@"id"]) { 14 self.itemID = value; 15 } 16 else if ([key isEqualToString:@"date"]) { 17 self.unixDate = [value integerValue]; 18 } 19 else 20 { 21 NSLog(@"RSSEntity: Undefined Key: %@", key); 22 [super setValue:value forKeyPath:key]; 23 } 24 return; 25 } 26 @end
这里最重要的部分就是对 setValuesForKeysWithDictionary:方法的调用,这个方法是 Objective-C KVC的技术重点,用来匹配类中与字典的键同名的属性,并将字典的值赋给该属性。
将JSON串对象化成 NSDictionary后,传给 setValuesForKeysWithDictionary:方法时,它会发送下面这消息(同时发送对应的值):setItemID, setName,setIconName,setUnixDate.所以对应的模型中,你只需要使用@property进行属性声明就可以了。
但是实际Coding设计中,与技术方案总有出入,JSON串中有 Key值为 "id",但在Objective-C的世界中, id是类属性,属于关键字,不能用来做变量名,所以为了解决这问题,我们可以重写[ setValue:(id)value forUndefinedKey:(NSString *)key ],对一些不能关联的Key值进行数据转移到我们想对应的属性值上。至此,我们将模型实体所需要初使化的任务交给了它自已,这样的结构是很好的架构设计,如果后期服务端返回的JSON串字段增加,我只需在模型头文件中增加对应的属性声明就可以了。
同时这段代码具有良好的防御性,如果服务器发送过来的JSON中的键名发生了变动,(有可能是未位程序猿手贱,又没测试), NSLog语句就会把未定义的键输出,如果因此导致你的APP Crash,等你Debug看日志的时候,你就可以去牛B的和服务端说,“兄弟,这个BUG,你看着办呗”,(和服务端调试API是我最不想干的事儿.)
如上的设计永远满足不了我们的要求,因为现在的数据交互中,JSON串永远不止我举例这么简单,比如:
1 { 2 "id":"20140308", 3 "name":"IOS Dev", 4 "iconName":"icon.png", 5 "date":1394254208, 6 "useInfo":{"name":"sherwin.chen","mail":"chensheng12330@gmail.com"} 7 }
这就是JSON嵌套对象了,应对这种IOS API提供了人性化设计,你可以重写 setValue:forKey:方法来处理这个问题。代码说事儿:
1.既然有新的对象,那么必须先创建该对象的实体.
1 @interface UserInfoEntity : Entity 2 @property (nonatomic, retain) NSString *name; 3 @property (nonatomic, retain) NSString *mail; 4 -(id) initWithDictionary:(NSDictionary*) jsonDict; 5 @end
2.在RSSEntity模型实体中加入属性设置器.
1 @property (nonatomic, retain) UserInfoEntity *userInfo;
3.在 RSSEntity.m 中重写 setValue:forKey:方法.
1 -(void)setValue:(id)value forKey:(NSString *)key 2 { 3 if ([key isEqualToString:@"userInfo"]) { 4 self.userInfo = [[[UserInfoEntity alloc] initWithDictionary:value] autorelease]; 5 } 6 else 7 { 8 [super setValue:value forKey:key]; 9 } 10 }
有了这样的结构,我们可以这样的访问RSSEntity实例化对象中的深层次对象:
1 [rss valueForKeyPath:@"userInfo.name"]; 2 [rss setValue:@"sherwin.chen" forKeyPath:"userInfo.name"];
如果你觉得这样设置不爽,你可以重写 setValue:forKeyPath: 根据路径,你可以做你想做的任何事儿,在此就不多述了. 此例只是用json举例(总有此公司架构喜欢口味重,还使用XML),如果这样,你也不必关心太多,只需将XML数据解析成 NSDictionary对象,关键是 KEY要对应,如此,万事大吉。
少即是多
网上一些资深的IOS开发者,对KVC讲述的比较多,但一般只是机制,真正在项目中应用还比较少,一些高级的技术也有它自身的弱点,如我上述的对KVC应用,也免要了要建立 JSON Key值的对应关系才能建议实体类模型,如果项目比较大,30以上的数量级的,还是很苦逼的。
懒人总有懒办法,<objc/runtime.h> 这是Apple为我们IOS开发者提供的好东西,一些你想不到的应用可以用它所提供的API进行处理。
利用反射机制我们可以将变量名字符串,强制插入到实例化对象中去,然后,你懂的......
object_setInstanceVariable:
object_getInstanceVariable:
这两个是好东西,有兴趣的可以去Coding实践一下,该篇文章只讲KVO.嘿嘿...
参考资料:
[IOS DOC For Apple.
IOS 6 Programming Pushing the Limits.]
转的话,标明个原地址呗: http://www.cnblogs.com/chensheng12330/p/3585489.html