zoukankan      html  css  js  c++  java
  • KVC 实战浅析

    KVC 就是 key value coding,废话!

    • 今天我们研究的是如何使用它!

      key value coding : 间接通过字符串类型的key取出对应的属性值

    KVC的价值

    • 1.可以访问私有成员变量的值
    • 2.可以间接修改私有成员变量的值(替换系统自带的导航栏、tabbar)
      举个例子:我现在要替换系统自带的tabbar,但是,系统的是“ readonly”的。解决方法是:KVC。
       // 替换系统的tabbar
       [self setValue:[[MyTabBar alloc] init] forKeyPath:@"tabBar"];
    • 我们点击@"tabBar" 进去之后会发现:是readonly 属性的。
      @property(nonatomic,readonly) UITabBar *tabBar NS_AVAILABLE_IOS(3_0); 
       // Provided for -[UIActionSheet showFromTabBar:]. Attempting to modify the contents of the tab bar directly will throw an exception.

    进一步了解

    keyPath包含了key的功能
    key:只能访问当前对象的属性
    keyPath:能利用.运算符一层一层往内部访问属性

    • 到底有啥好用???
      KVC 可是Cocoa的一大招!为啥我这么说呢?
    • 下面请看一个例子:

      @interface DataModel : NSObject
      @property (nonatomic,strong)NSString *applicationId;
      @property (nonatomic,strong)NSString *slug;
      @property (nonatomic,strong)NSString *name;
      @property (nonatomic,strong)NSString *releaseDate;
      @property (nonatomic,strong)NSString *version;
      @property (nonatomic,strong)NSString *descriptionStr;//现在和json,key不一致
      @property (nonatomic,assign)int       categoryId;
      @property (nonatomic,strong)NSString *categoryName;
      @property (nonatomic,strong)NSString *iconUrl;
      @property (nonatomic,strong)NSString *itunesUrl;
      @property (nonatomic,strong)NSString *starCurrent;
      @property (nonatomic,strong)NSString *starOverall;
      @property (nonatomic,strong)NSString *ratingOverall;
      @property (nonatomic,strong)NSString *downloads;
      @property (nonatomic,strong)NSString *currentPrice;
      @property (nonatomic,strong)NSString *lastPrice;
      @property (nonatomic,strong)NSString *priceTrend;
      @property (nonatomic,strong)NSString *expireDatetime;
      @property (nonatomic,strong)NSString *releaseNotes;
      @property (nonatomic,strong)NSString *updateDate;
      @property (nonatomic,strong)NSString *fileSize;
      @property (nonatomic,strong)NSString *ipa;
      @property (nonatomic,strong)NSString *shares;
      @property (nonatomic,strong)NSString *favorites;
      
      @end
    • 以上的model 不少了吧!我们开发时不可能像下面那样,一个一个地写吧!太Low了:
       DataModel *model = [[DataModel alloc] init];
       cell.xxx.text = model.name;
       cell.xxx.text = model.descriptionStr;
       xxx.xxx.xxx = model. applicationId;
        ...
        ...

      那么怎么样才能一次性解决掉这个问题呢?---用KVC

    • 使用KVC 的步骤非常简单,要记住!就是重写2个方法而已!
    • 重写2个方法:

      -(void)setValue:(id)value forUndefinedKey:(NSString *)key{ }
      
      -(id)valueForUndefinedKey:(NSString *)key{
        return nil;
      }

      下面是常见使用:

    • KVC的前提是保证 模型里的属性要和JSON 解析的值一致!
    • 上面的模型属性里有一个和json解析不一样的字段,descriptionStr;//现在和json,key不一致 ,json解析的是description。
    • 不一致的原因是模型的属性尽量不要和系统的关键字一致!所以,我们模型要避开关键字。那么问题来了,我们怎么设置值一致呢?
    • 很简单!!!只需在重写的方法里替换就OK啦!

      -(void)setValue:(id)value forUndefinedKey:(NSString *)key{
      
       //找到和属性不一致名字的key,然后赋值给self的属性
        if ([key isEqualToString:@"description"]) {
      
      // self.descriptionStr = value; // 不推荐使用
          [self setValue:value forKey:@"descriptionStr"]; // 推荐
        }
      }
    • 例如:id、description等等系统关键字就可以用这种方法做了。
      上面是解决关键字,也就是属性名字和系统关键字有冲突!!!,下面介绍的,也是开发中经常遇到的问题:就是对于处理“类型”,就用下面的方法:
    • 类型?
    • 这就是类型:服务器返回有“引号”和没引号,分别代表 NSString 和NSNumber

      看有没有引号.png
    • 这个对KVC 也有影响,下面我带大家 处理类型!!!
    • 事实上,在企业服务器开发人员把 NSString修改成 NSNumber 类型或其它类型,他们通常是不会主动告诉你的!这是你之前可以成功运行的APP突然崩溃!这你又得花费很多时间去查找!为了避免此问题和如何处理,请看下面:
    • 重写方法:
      - (void)setValue:(id)value forKey:(NSString *)key{}
    • 这里就以 NSNumber 为例子:

      - (void)setValue:(id)value forKey:(NSString *)key
      {
        // 服务器是 NSNumber ,模型表里是 NSString类型,所以,要处理
      if ([value isKindOfClass:[NSNumber class]]) {
      
         // NSNumber--> NSString
         [self setValue:[NSString stringWithFormat:@"%@",value] forKey:key];
      
      }else{
          [super setValue:value forKey:key];
         }
      }

    总结:使用KVC时,最好重写2个方法 和 一个处理 类型的方法;

    • 下面再次回顾上面的方法使用:

      #pragma mark ---- 一定要实现下面的两个方法!(属性名字用这个方法)
        /**
       利用kvc解析json,一定要实现下面的两个方法!(属性名字用这个方法)
       */
      -(void)setValue:(id)value forUndefinedKey:(NSString *)key{
        //找到和属性不一致名字的key,然后赋值给self的属性
        if ([key isEqualToString:@"description"]) {
      
      // self.descriptionStr = value; // 不推荐使用
        [self setValue:value forKey:@"descriptionStr"]; // 推荐
        }
      }
      -(id)valueForUndefinedKey:(NSString *)key{
          return nil;
      }
    
      #pragma mark --- 对于处理“类型”,就用下面的方法
    
      // 处理特殊 ----(类型)例如:NSNumber--> NSString
      - (void)setValue:(id)value forKey:(NSString *)key
    {
    // price 服务器是 NSNumber
    // 服务器是 NSNumber ,模型表里是 NSString类型,所以,要处理
    if ([value isKindOfClass:[NSNumber class]]) {
    
        // NSNumber--> NSString
        [self setValue:[NSString stringWithFormat:@"%@",value] forKey:key];
    
    }else{
        [super setValue:value forKey:key];
      }
    }
    • 其实KVC的使用场景还有,这里就不介绍了!这个是在开发中非常常用的!!!
    • 你感受到了KVC好处了吗?



    原文链接:http://www.jianshu.com/p/7e963c35f5fb
     
     
     

     IOS中KVC与KVO的应用解析 一、NSKeyValueCoding(KVC) 1、从一个小例子引入 KVC键值编码是Object-C为我们提供的一种对成员变量赋值的方法。在探讨其方法之前,我们先来看一个小例子: 首先,创建一个数据模型model类: ? 1 2 3 4 5 6 7 8...

    IOS中KVC与KVO的应用解析

    一、NSKeyValueCoding(KVC)

    1、从一个小例子引入

    KVC键值编码是Object-C为我们提供的一种对成员变量赋值的方法。在探讨其方法之前,我们先来看一个小例子:

    首先,创建一个数据模型model类:

    1
    2
    3
    4
    5
    6
    7
    8
    //.h文件
    #import <Foundation/Foundation.h>
    @interface Model : NSObject
    {
        @public//将成员变量设置为公有的 以便其他文件有访问权限
        NSString * str;
    }
    @end

    我们在其他文件中有两种方法str进行赋值和取值:

    1
    2
    3
    4
    5
        Model * model = [[Model alloc]init];
        model->str=@"312";//普通方法赋值
        [model setValue:@"321" forKey:@"str"];//kvc赋值
        NSLog(@"%@",model->str);//普通方法取值
        NSLog(@"%@",[model valueForKey:@"str"]);//kvc取值

    同样的,对于用@property声明的变量,使用kvc的效果和使用点语法,setter,getter方法的效果是一样的。

    2、KVC有关函数方法详解

    通过上面的例子,我们已经可以简单了解KVC是干什么的了,下面是一些常用方法。

    + (BOOL)accessInstanceVariablesDirectly;

    这个方法类似一个开关,默认返回为YES,表示支持KVC方式赋值,也可以在子类中将其重写,如果返回为NO,则再进行KVC会抛出异常。

    - (id)valueForKey:(NSString *)key;

    通过键取值

    - (void)setValue:(id)value forKey:(NSString *)key;

    通过字符串键给成员变量赋值

    - (BOOL)validateValue:(inout id *)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

    系统默认实现的方法,验证一个键值是否有效

    - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

    将取到的值放入一个可变数组中

    - (NSMutableOrderedSet *)mutableOrderedSetValueForKey:(NSString *)key NS_AVAILABLE(10_7, 5_0);

    将取到的值放入可变的有序集合中

    - (NSMutableSet *)mutableSetValueForKey:(NSString *)key;

    将取到的值放入可变的集合中

    - (id)valueForKeyPath:(NSString *)keyPath;
    - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;

    上面这两个方法分别是通过路径赋值与取值,数据结构类似地图,比如在model类中有一个成员变量model2,在Model2类中有一个字符串,我们可以通过如下的方式赋值取值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //Model.h
    #import "Model2.h"
    @interface Model : NSObject
    {
        @public
        NSString * str;
        Model2 * model2;
    }
    //Model2.h
    @interface Model2 : NSObject
    {
    @public
        NSString * str2;
    }
    @end
    //其他文件
        Model * model = [[Model alloc]init];
        Model2 * model2 = [[Model2 alloc]init];
        model->model2=model2;
        [model setValue:@"123" forKeyPath:@"model2.str2"];
        NSLog(@"%@",[model valueForKeyPath:@"model2.str2"]);

    - (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;
    - (NSMutableOrderedSet *)mutableOrderedSetValueForKeyPath:(NSString *)keyPath NS_AVAILABLE(10_7, 5_0);
    - (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;

    上面三个方法与前面类似,只是是从路径取值的。

    - (id)valueForUndefinedKey:(NSString *)key;

    这个方法可以获取没有提前定义的成员变量的值,比如运行时创建的,下面这个方法是给未定义的成员变量赋值

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

    注意:这两个方法默认的实现会抛出异常,子类必须重写才能使用。

    - (void)setNilValueForKey:(NSString *)key;

    将成员变量置为nil

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

    根据键值获取键值对字典

    - (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues;

    通过字典对成员变量同意赋值,经常使用

    二、NSKeyValueObservingCustomization(KVO)

    KVO是一种消息监听机制,可以在某个量发生变化的时候将消息传送给监听者,因此广泛用于传值,界面低耦合等逻辑中。KVO机制的核心是以下三个方法:

    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

    使用这个方法注册一个监听者,参数解释如下:

    observer:监听者对象

    keyPath:监听的参数

    options:监听选项

    context:参数传递

    监听的选项枚举如下:

    1
    2
    3
    4
    5
    6
    7
    8
    typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {
        NSKeyValueObservingOptionNew = 0x01,//回调的字典中存放新值
        NSKeyValueObservingOptionOld = 0x02,//回调的字典中存放旧值
        NSKeyValueObservingOptionInitial ,//值改变前进行回调
        NSKeyValueObservingOptionPrior//改变前后都进行回调
     
    };
    //回调字典后面会解释

    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context ;
    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

    这两个方法都是用来移除监听者

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

    这个方法是监听对象数据改变时回调的方法,change是一个字典,字典中根据监听的选项不同,存放不同的值(新或者旧)。context是传递的参数。

    代码示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
         model = [[Model alloc]init];
        //添加监听者
        [model addObserver:self forKeyPath:@"str" options:NSKeyValueObservingOptionNew context:@"321"];
        [model setValue:@"qw" forKey:@"str"];
    }
     
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
        if ([keyPath isEqualToString:@"str"]) {
            NSLog(@"%@",context);
        }
    }

     

  • 相关阅读:
    HTML5 实现Link跳线效果
    在TWaver的Tree节点上画线
    用TWaver加载大型游戏场景一例
    22万个木箱!TWaver 3D极限压榨
    如何在MONO 3D寻找最短路路径
    如何创建TWaver 3D的轮廓选中效果
    巧用TWaver 3D 矢量图形功能
    如何实现TWaver 3D颜色渐变
    HDU 1390 Binary Numbers
    HDU 1328 IBM Minus One
  • 原文地址:https://www.cnblogs.com/Jenaral/p/5531882.html
Copyright © 2011-2022 走看看