iOS - KVC 键值编码

1、KVC

  • KVC 是 Key-Value Coding 的简写,是键值编码的意思,属于 runtime 方法。Key Value Coding 是 cocoa 的一个标准组成部分,是间接给对象属性设置数值的方法,它能让我们可以通过 name(key) 的方式访问属性变量, 不必调用明确的属性访问方法, 如我们有个属性变量叫做 foo, 我们可以 foo 直接访问它,同样我们也可以用 KVC 来完成 [Object valueForKey:@“foo”], 这样做主要的好处就是来减少我们的代码量。
  • 程序执行过程中,KVC 动态给对象属性设置数值,不关心属性在 .h 和 .m 中是如何定义的,只要对象有属性,就能够读取和设置。这种方式,有点违背程序的开发原则。
  • 在 iOS 中,用 KVC 用的最多是核心动画,核心动画是通过 KVC 对涂层的可动画属性设置数值来实现的。

2、数据模型

  • 模型是专门用来存放数据的对象,一般都是一些直接继承自 NSObject 的纯对象,内部会提供一些属性来存放数据,控制器可以直接传递模型给视图控件以显示空间的内容。
  • 1)用模型取代字典的好处
    • 使用字典的坏处:

      • 一般情况下,设置数据和取出数据都使用 “字符串类型的 key”,编写这些 key 时,编辑器没有智能提示,需要手敲,手敲字符串 key,key 容易写错,Key 如果写错了,编译器不会有任何警告和报错,造成设错数据或者取错数据。如:

            dict[@"name"] = @"Jack";
            NSString *name = dict[@"name"];
    • 使用模型的好处:
      • 所谓模型,其实就是数据模型,专门用来存放数据的对象,用它来表示数据会更加专业。模型设置数据和取出数据都是通过它的属性,属性名如果写错了,编译器会马上报错,因此,保证了数据的正确性
        使用模型访问属性时,编译器会提供一系列的提示,提高编码效率。

            app.name = @"Jack";
            NSString *name = app.name;
  • 2)字典转模型
    • 字典转模型的过程最好封装在模型内部。
    • 模型应该提供一个可以传入字典参数的构造方法。
          - (instancetype)initWithDict:(NSDictionary *)dict;
          + (instancetype)xxxWithDict:(NSDictionary *)dict;
  • 3)字典转模型 KVC 方法
    • 字典转模型:setValuesForKeysWithDictionary

      • 字典中的 key 值需与要赋值的对象的属性变量名相同,并且都为字符串类型。
    • 模型转字典:dictionaryWithValuesForKeys
      • 参数是要被转换到字典中的属性名称
  • 4)KVC 数据模型的设置
    • 为了避免服务端返回的数值型数据是 null,可以把数值型的数据设置成 NSNumber 类型,否则会报错 could not set nil as the value for the key messageId 。
    • id 是服务端最喜欢用的属性,id 在 iOS 中是关键字,但在模型中可以正常使用的。
    • copy 属性,在设置数值的时候,如果有一方是可变的,会默认做一次 copy 操作,会建立新的副本,在模型中对象全都是用 copy 属性会比较安全。
    • 定义为 copy 的属性,重写了 setter 方法之后,定义属性的 copy 就是摆设了,不会默认进行 copy 操作,必须要自己 copy 一下,否则设置数值的时候,不会 copy。
  • 5)字典转模型的过程

3、KVC 赋值与取值

  • KVC 是一种操作全局变量的方法,无论是公有的,私有的,还是受保护的全都可以操作。

    • 1、找对象的 setter 方法,找到就执行;
    • 2、找不到 setter 方法就找 _name 变量,找到就赋值;
    • 3、如果找不到 _name 变量,就找 name;
    • 4、如果 name 也找不到就会让对象调用 -(void)setValue:forUnderfinedKey; 方法处理异常。
  • Objective-C
    • KvcClass.h

          @property(nonatomic, assign) NSInteger ID;
      
          @property(nonatomic, copy) NSString *name;
          @property(nonatomic, assign) NSInteger age;
      
          @property(nonatomic, retain) SubKvcClass *subKVC;
    • SubKvcClass.h
          @property(nonatomic, copy) NSString *subName;
          @property(nonatomic, assign) NSInteger subAge;
    • ViewController.m
          KvcClass *kvcObject = [[KvcClass alloc] init];
      
          SubKvcClass *subKVCObject = [[SubKvcClass alloc] init];
          kvcObject.subKVC = subKVCObject;
  • Swift
    • KvcClass.swift

          var ID:NSInteger!
      
          var name:String!
          var age:NSInteger = 0
      
          var subKVC:SubKvcClass!
    • SubKvcClass.swift
          var subName:String!
          var subAge:NSInteger = 0
    • ViewController.swift
          var kvcObject = KvcClass()
      
          var subKVCObject = SubKvcClass()
          kvcObject.subKVC = subKVCObject

3.1 通过 键值编码 给对象的属性动态赋值

  • 必须得有标准的 getter 和 setter 方法,或者用 @property 声明。
  • 调用 setValue: forKey: 方法以字符串的方式向对象发送消息,设置实例变量的值。第一个参数是要设置的值,第二个参数是实例变量的名称。
  • 调用 valueForKey: 来获取实例变量的值。
  • Objective-C
        // 动态设置属性的值
        [kvcObject setValue:@"xiao bai" forKey:@"name"];
        [kvcObject setValue:@"8" forKey:@"age"];
    
        // 获取实例变量的值
        NSString *name = [kvcObject valueForKey:@"name"];
        NSInteger age = [[kvcObject valueForKey:@"age"] integerValue];
  • Swift
        // 动态设置属性的值
        kvcObject.setValue("xiao bai", forKey: "name")
        kvcObject.setValue("8", forKey: "age")
    
        // 获取实例变量的值
        let name1 = kvcObject.valueForKey("name") as! String
        let age1 = kvcObject.valueForKey("age") as! NSInteger

3.2 通过 键路径 给实例变量是其他类的对象赋值

  • 如果实例变量中有其他类的对象,那么可以使用 setValue: forKeyPath: 给其他类的对象的属性变量赋值。
  • Objective-C
        // 通过键路径给 KVCClass 中的对象的属性赋值
        [kvcObject setValue:@"sub xiao bai" forKeyPath:@"subKVC.subName"];
        [kvcObject setValue:@"5" forKeyPath:@"subKVC.subAge"];
    
        // 获取 KVCClass 中的对象的属性值
        NSString *subName = [kvcObject valueForKeyPath:@"subKVC.subName"];
        NSInteger subAage = [[kvcObject valueForKeyPath:@"subKVC.subAge"] integerValue];
  • Swift
        // 通过键路径给 KvcClass 中的对象的属性赋值
        kvcObject.setValue("sub xiao bai", forKeyPath: "subKVC.subName")
        kvcObject.setValue("5", forKeyPath: "subKVC.subAge")
    
        // 获取 KvcClass 中的对象的属性值
        let subName1 = kvcObject.valueForKeyPath("subKVC.subName") as! String
        let subAage1 = kvcObject.valueForKeyPath("subKVC.subAge") as! NSInteger

3.3 通过 字典 给对象的属性动态赋值

  • 字典中的 key 值需与要赋值的对象的属性变量名相同。并且都为字符串类型。

        - (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
        - (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
  • Objective-C
        NSDictionary *kvcDic = @{@"name":@"xiaobai", @"age":@"6"};
    
        // 以字典的 key 和 value 值分别作为 kvc 的 key 和 value 存储
        [kvcObject setValuesForKeysWithDictionary:kvcDic];
    
        // 取值,获取指定 keys 值对应的 values
        NSDictionary *dicValue = [kvcObject dictionaryWithValuesForKeys:@[@"name", @"age"]];
  • Swift
        let kvcDic = ["name":"xiaobai", "age":"6"]
    
        // 以字典的 key 和 value 值分别作为 kvc 的 key 和 value 存储
        kvcObject.setValuesForKeysWithDictionary(kvcDic)
    
        // 取值,获取指定 keys 值对应的 values
        let dicValue = kvcObject.dictionaryWithValuesForKeys(["name", "age"])

4、KVC 异常处理

4.1 数据冗余处理

  • 在键值编码的类中使用以下两个方法处理 key 值不存在的异常。如果不做处理,编译时系统会报错。

        - (void)setValue:(id)value forUndefinedKey:(NSString *)key;
        - (id)valueForUndefinedKey:(NSString *)key;
  • Objective-C
    • KvcClass.m

          // 设置不存在 key 的值
          - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
      
              NSLog(@"key 值 %@ 不存在,无法设置值 !", key);
          }
      
          // 获取不存在的 key 的值
          - (id)valueForUndefinedKey:(NSString *)key {
      
              NSLog(@"key 值 %@ 不存在,无法获取值 !", key);
      
              return nil;
          }
    • ViewController.m
          // 对象 kvcObject 没有 score 属性,出现数据异常
          [kvcObject setValue:@"99" forKey:@"score"];
          [kvcObject valueForKey:@"score"];
      
          NSDictionary *kvcDic1 = @{@"name":@"xiaobai", @"age":@"6", @"score":@"100"};
      
          // 对象 kvcObject 没有 score 属性,出现数据异常
          [kvcObject setValuesForKeysWithDictionary:kvcDic1];                                     
  • Swift
    • KvcClass.swift

          // 设置不存在 key 的值
          override func setValue(value: AnyObject?, forUndefinedKey key: String) {
      
              print("key 值 \(key) 不存在,无法设置值 !")
          }
      
          // 获取不存在的 key 的值
          override func valueForUndefinedKey(key: String) -> AnyObject? {
      
              print("key 值 \(key) 不存在,无法获取值 !")
      
              return nil
          }
    • ViewController.swift
          // 对象 kvcObject 没有 score 属性,出现数据异常
          kvcObject.setValue("99", forKey: "score")
          kvcObject .valueForKey("score")
      
          // 对象 kvcObject 没有 score 属性,出现数据异常
          let kvcDic1 = ["name":"xiaobai", "age":"6", "score":"99"]
          kvcObject.setValuesForKeysWithDictionary(kvcDic1)

4.2 key 为系统关键字处理

  • 在需要处理的数据源中有系统关键字时,在键值编码处理的类中使用以下两个方法处理 key 值为系统关键字的情况。

        - (void)setValue:(id)value forUndefinedKey:(NSString *)key;
        - (id)valueForUndefinedKey:(NSString *)key;
  • id 是服务端最喜欢用的属性,id 在 iOS 中是关键字,但在模型中可以正常使用的。
  • Objective-C
    • KvcClass.m

          // 设置不存在 key 的值
          - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
      
              // id 为系统关键字,用 ID 代替与系统冲突的 id
              if ([key isEqualToString:@"id"]) {
      
                  // 设置自定义的 key 的值
                  self.ID = [(NSString *)value integerValue];
              }
          }
      
          // 获取不存在的 key 的值
          - (id)valueForUndefinedKey:(NSString *)key {
      
              // id 为系统关键字,用 ID 代替与系统冲突的 id
              if ([key isEqualToString:@"id"]) {
      
                  // 获取自定义的 key 的值
                  return [NSString stringWithFormat:@"%li", self.ID];
              }
      
              return nil;
          }
    • ViewController.m
          // id 为系统关键字
          [kvcObject setValue:@"3" forKey:@"id"];
          NSInteger ID1 = [[kvcObject valueForKey:@"id"] integerValue];
      
          NSDictionary *kvcDic2 = @{@"name":@"xiaobai", @"age":@"6", @"id":@"5"};
          [kvcObject setValuesForKeysWithDictionary:kvcDic2];
          NSInteger ID2 = kvcObject.ID;
  • Swift
    • KvcClass.swift

          // 设置不存在 key 的值
          override func setValue(value: AnyObject?, forUndefinedKey key: String) {
      
              // id 为系统关键字,用 ID 代替与系统冲突的 id
              if key == "id" {
      
                  // 设置自定义的 key 的值
                  self.ID = (value as! NSString).integerValue
              }
          }
      
          // 获取不存在的 key 的值
          override func valueForUndefinedKey(key: String) -> AnyObject? {
      
              // id 为系统关键字,用 ID 代替与系统冲突的 id
              if key == "id" {
      
                  // 获取自定义的 key 的值
                  return NSString(format: "%li", self.ID)
              }
      
              return nil
          }
    • ViewController.swift
          // id 为系统关键字
          kvcObject.setValue("3", forKey: "id")
      
          let ID1 = (kvcObject.valueForKey("id") as! NSString).integerValue
      
          // id 为系统关键字
          let kvcDic2 = ["name":"xiaobai", "age":"6", "id":"5"]
      
          kvcObject.setValuesForKeysWithDictionary(kvcDic2)
          let ID2 = kvcObject.ID

5、KVC 消息传递

  • valueForKey: 的使用并不仅仅用来取值那么简单,还有很多特殊的用法,集合类也覆盖了这个方法,通过调用 valueForKey: 给容器中每一个对象发送操作消息,并且结果会被保存在一个新的容器中返回,这样我们能很方便地利用一个容器对象创建另一个容器对象。另外,valueForKeyPath: 还能实现多个消息的传递。
  • Objective-C
        NSArray *array = @[@"10.11", @"20.22"];
    
        // 结果为 (10, 20)
        NSArray *resultArray = [array valueForKeyPath:@"doubleValue.intValue"];
  • Swift
        let array:NSArray = ["10.11", "20.22"]
    
        // 结果为 (10, 20)
        let resultArray:AnyObject? = array.valueForKeyPath("doubleValue.intValue")

6、KVC 字典转模型 数据冗余处理

  • 字典中元素与模型中的属性数量不想等的情况处理。处理 key 值不存在的异常。如果不做处理,编译时系统会报错。

6.1 系统方式

  • Objective-C

        + (instancetype)newsModelWithDict:(NSDictionary *)dict {
            id obj = [[self alloc] init];
    
            [obj setValuesForKeysWithDictionary:dict];
    
            return obj;
        }
    
        /// 重写系统方法
    
        - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    
        }

6.2 列举属性数组方式

  • Objective-C

        + (instancetype)newsModelWithDict:(NSDictionary *)dict {
            id obj = [[self alloc] init];
    
            [obj setValueWithDict:dict];
    
            return obj;
        }
    
        - (instancetype)setValueWithDict:(NSDictionary *)dict {
    
            // 列出所有使用的属性
            NSArray *properties = @[@"title", @"digest", @"imgsrc", @"replyCount"];
    
            for (NSString *key in properties) {
    
                // 判断字典中是否包含 key
                if (dict[key] != nil) {
    
                    // 每一个属性使用 kvc 设置数值
                    [self setValue:dict[key] forKey:key];
                }
            }
            return self;
        }

6.3 运行时动态获取对象属性方式

  • Objective-C

        + (instancetype)newsModelWithDict:(NSDictionary *)dict {
            id obj = [[self alloc] init];
    
            [obj setValueWithDict:dict];
    
            return obj;
        }
    
        /// 使用运行时动态获取对象属性
    
        - (instancetype)setValueWithDict:(NSDictionary *)dict {
    
            unsigned int count = 0;
    
            // 拷贝对象属性数组(数组名就是指向数组第一个元素的地址)
            objc_property_t *properties = class_copyPropertyList(self.class, &count);
    
            // 遍历数组
            for (unsigned int i = 0; i < count; ++i) {
    
                // 从数组中获取属性
                objc_property_t pty = properties[i];
    
                // 获取属性名称
                const char *cname = property_getName(pty);
    
                NSString *key = [NSString stringWithUTF8String:cname];
    
                if (dict[key] != nil) {
                    [self setValue:dict[key] forKey:key];
                }
            }
    
            // 释放数组
            free(properties);
    
                return self;
            }
时间: 2024-10-26 03:13:58

iOS - KVC 键值编码的相关文章

Property属性,&amp;nbsp;KVC键值编码OC…

1.属性:帮你自动生成setter 和 getter 方法 属性的声明:(写在.h中) 格式: @property 数据类型 属性名 属性的实现:(写在.m中) 格式: @synthesize 属性名: 例子1: Person.h @interface Person : NSObject //属性的声明:属性:属性名就是实例变量名去掉下划线@property NSString *gender; @end Person.m //属性的实现 @synthesize gender; @end main

Property属性,&amp;amp;nbsp;KVC键值编码OC…

1.属性:帮你自动生成setter 和 getter 方法      属性的声明:(写在.h中)      格式: @property 数据类型 属性名      属性的实现:(写在.m中)      格式: @synthesize 属性名: 例子1: Person.h @interface Person : NSObject //属性的声明:属性:属性名就是实例变量名去掉下划线@property NSString *gender; @end Person.m //属性的实现 @synthesi

obj-c编程16:键值编码(KVC)

    我们可以借助obj-c中的键值编码(以后简称KVC,Key-Value Coding)来存取类的属性,通过指定所要访问的属性名字符串标示符,可以使用存取器方法来获取或设置类的属性.下面的例子,借助于KVC的功能读取和设置了类Son实例的属性i: #import <Foundation/Foundation.h> #define msg(...) NSLog(__VA_ARGS__) #define mki(x) [NSNumber numberWithInt:x] @interface

键值编码 Key-Value Coding Programming Guide

1,什么是Key-Value Coding? Key-Value Coding是一种间接访问对象属性的机制,使用字符串标识属性,而不是通过调用实例变量的访问方法.其使用的方法基本都声明自NSKeyValueCoding协议,并被NSObject实现. Key-Value Coding支持对象属性,也支持标量类型和结构类型.非对象参数和返回类型被自动包装和解包装. NSKeyValueCoding定义的方法有: 获得属性值的方法:     – valueForKey:     – valueFor

iOS - KVO 键值观察

1.KVO KVO 是 Key-Value Observing 的简写,是键值观察的意思,属于 runtime 方法.Key Value Observing 顾名思义就是一种 observer 模式用于监听属性变量值的变化,也是运行时的方法,当实例变量改变时,系统会自动采取一些动作.KVO 跟 NSNotification 有很多相似的地方,用 addObserver:forKeyPath:options:context: 去 start observer, 用 removeObserver:f

iOS网络编程入门:iCloud键值数据存储编程实例

iCloud键值数据存储设计 iCloud键值数据存储编程实例,画面中有两个开关控件,左图是设备1点击"设置iCloud数据"按钮,将控件状态保存到iCloud服务器.右图是设备2画面,过几秒钟后设备2收到变更通知. 配置Xcode工程 使用Xcode创建一个iOS工程,工程创建好之后,选择TAGETS→MyNotes→Summary→Entitlements,我们可以在这里配置授权信息. 然后我们还需要应用设置代码签名标识,代码签名标识需要选择这个配置概要文件的.选择TAGETS→M

iOS网络编程-iCloud键值数据存储编程实例

iCloud键值数据存储设计 iCloud键值数据存储编程实例,画面中有两个开关控件,左图是设备1点击"设置iCloud数据"按钮,将控件状态保存到iCloud服务器.右图是设备2画面,过几秒钟后设备2收到变更通知.     配置Xcode工程 使用Xcode创建一个iOS工程,工程创建好之后,选择TAGETS→MyNotes→Summary→Entitlements,我们可以在这里配置授权信息. 然后我们还需要应用设置代码签名标识,代码签名标识需要选择这个配置概要文件的.选择TAGE

《iOS组件与框架——iOS SDK高级特性剖析》——第8章,第8.6节键值存储同步

8.6 键值存储同步 iOS组件与框架--iOS SDK高级特性剖析 iCloud还支持键值存储同步.这类似于在NSMutableDictionary或NSUserDefaults中存储信息,将一个与对象值相关联的键用于存储和检索:iCloud键值存储的不同之处在于,将自动在设备之间同步键和值.本书编写期间,iCloud为每个应用提供1MB的键值存储空间,最多可存储1024个键值对,因此键值存储机制只适合用于存储少量信息. 本章的示例应用利用iCloud键值存储来跟踪最后修改的备忘录.详细视图控

iOS中 KVO 键值观察者

KVO Key-Value-Obsever 键值观察者 1.首先要有一个观察者,此时被观察者是自己找一个观察者观察自己的key值对应的value值有没有改变,如果改变了就可以做一些响应的操作 创建一个被观察者: self.person = [[Person alloc]init]; 2.给被观察者注册观察者 NSKeyValueObservingOptionNew 当重新给被观察者的key值赋值时,触发监听方法,此时打印的 change 永远都是最新的值 存放的是value值 NSKeyValu