zoukankan      html  css  js  c++  java
  • IOS 之 KVC 数据模型应用

      引言: 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

  • 相关阅读:
    什么是mybatis
    Oracle存储过程、游标、函数
    Oracle事务、视图、序列
    异步发送表单数据到JavaBean,并响应JSON文本返回
    结果的转发和重定向
    在业务控制方法中收集List<JavaBean>参数
    在业务控制方法中收集数组参数
    在业务控制方法中写入包装User的模型来收集参数
    在业务控制方法中写入User,Admin多个模型收集参数
    Linux内核分析:实验八--Linux进程调度与切换
  • 原文地址:https://www.cnblogs.com/chensheng12330/p/3585489.html
Copyright © 2011-2022 走看看