iOS开发基础14-KVC的应用与底层逻辑

在 iOS 开发中,Key-Value Coding (KVC) 是一种十分强大的访问和修改对象属性的技术。通过 KVC,我们可以在运行时以字符串的形式直接访问属性,从而更灵活地操作对象。本文将详细介绍如何使用 KVC 进行数据的存取、字典转模型、模型转字典,以及一些高级应用。

一、KVC 简介

KVC 是一个非正式协议,它允许通过字符串键(Key)来访问对象的属性。相对于直接调用 setter 和 getter 方法,KVC 提供了一种更为动态的方式来操作对象的属性值。这对于例如字典与模型之间的转换,特别有用。

二、KVC 存数据

1. 基本属性的存取

我们定义一个 Person 类以及一个 Dog 类,用于演示 KVC 的使用。

Person.h

#import <Foundation/Foundation.h>

@class Dog;

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) double money;
@property (nonatomic, strong) Dog *dog;

@end

Person.m

#import "Person.h"

@implementation Person
{
    @private
    int _age;
}

- (void)say {
    NSLog(@"age = %i", _age);
}

@end

Dog.h

#import <Foundation/Foundation.h>

@interface Dog : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) double price;

@end

Dog.m

#import "Dog.h"

@implementation Dog

@end

2. 使用 KVC 存数据

基本属性赋值

我们可以通过 KVC 来为 Person 对象的属性赋值。

Person *p = [[Person alloc] init];

// 使用 KVC 为基本属性赋值
[p setValue:@"lmj" forKey:@"name"];
[p setValue:@(668.0) forKey:@"money"];

多层赋值

对于嵌套对象的属性,我们可以使用 setValue:forKeyPath: 方法来进行赋值。

p.dog = [[Dog alloc] init];
[p setValue:@"xiaoqiang" forKeyPath:@"dog.name"];
[p setValue:@(110.0) forKeyPath:@"dog.price"];

给私有成员变量赋值

KVC 甚至可以突破 @private 修饰符的限制,直接访问私有成员变量。

[p setValue:@(30) forKey:@"_age"];
[p say]; // age = 30

字典转模型

KVC 还可以用于字典转模型,通过 setValuesForKeysWithDictionary: 方法可以将字典中的值赋予对象对应的属性。

NSDictionary *dict = @{
    @"name": @"xxx",
    @"money": @(998.1),
    @"dog": @{ @"name": @"wangcai", @"price": @(110.0) }
};

// 使用 KVC 进行字典转模型
[p setValuesForKeysWithDictionary:dict];

NSLog(@"name = %@, money = %f", p.name, p.money); // getter

三、KVC 取数据

使用 KVC,我们不仅可以设置属性,还可以获取属性值。

1. 获取单个值

使用 valueForKey: 获取单个属性值。

NSString *name = [p valueForKey:@"name"];
double money = [[p valueForKey:@"money"] doubleValue];

NSLog(@"name = %@, money = %f", name, money);

2. 多层获取值

对于嵌套对象的属性,可以使用 valueForKeyPath: 方法。

NSString *dogName = [p valueForKeyPath:@"dog.name"];
NSLog(@"dogName = %@", dogName);

3. 模型转字典

通过 dictionaryWithValuesForKeys: 方法将对象转换为字典。

NSDictionary *dict = [p dictionaryWithValuesForKeys:@[@"name", @"money"]];
NSLog(@"dict = %@", dict);

4. 获取数组中对象的值

如果数组中的元素都是相同类型的对象,可以使用 KVC 获取所有对象的某个属性值。

Person *p1 = [[Person alloc] init]; p1.name = @"zs"; p1.money = 111;
Person *p2 = [[Person alloc] init]; p2.name = @"ls"; p2.money = 222;
Person *p3 = [[Person alloc] init]; p3.name = @"ww"; p3.money = 666;

NSArray *arr = @[p1, p2, p3];
NSArray *names = [arr valueForKeyPath:@"name"];

NSLog(@"names = %@", names);

5. KVC 运算符

KVC 支持一些简单的集合运算符,比如获取数组中某个属性的平均值、最大值、最小值等。

// 平均值
id avgMoney = [arr valueForKeyPath:@"@avg.money"];
// 最大值
id maxMoney = [arr valueForKeyPath:@"@max.money"];
// 最小值
id minMoney = [arr valueForKeyPath:@"@min.money"];

NSLog(@"avgMoney = %@, maxMoney = %@, minMoney = %@", avgMoney, maxMoney, minMoney);

四、KVC 底层逻辑分析

1. valueForKey: 和setValue:forKey:

  • valueForKey: 方法内部执行流程:

    1. 查找同名的 getter 方法。
    2. 查找同名的带下划线 _ 属性。
    3. 查找 accessInstanceVariablesDirectly 方法,如果返回 YES,则直接访问同名或带下划线的实例变量。
    4. 如果上述查找均失败,则执行 valueForUndefinedKey: 方法。
  • setValue:forKey: 方法内部执行流程:

    1. 查找同名的 setter 方法。
    2. 查找同名的带下划线 _ 属性。
    3. 查找 accessInstanceVariablesDirectly 方法,如果返回 YES,则直接访问同名或带下划线的实例变量。
    4. 如果上述查找均失败,则执行 setValue:forUndefinedKey: 方法。

2. Key-Value Observing (KVO) 交互

KVC 和 KVO 通常一起使用。当通过 KVC 修改属性时,属性的变更会触发 KVO 回调,从而允许响应式编程。

3. 字典转模型与模型转字典

通过 KVC,可以快速进行字典与模型之间的数据转换,大大简化了数据的过滤和处理流程。

总结:

KVC 作为一种灵活强大、高效便捷的编程技术,通过提供统一的接口实现对于对象属性的访问与操作,使得开发过程中实现模型和视图之间的数据绑定变得更加轻松。此外,通过 KVC 方法内部流程和原理的深层分析,我们可以理解并合理利用这一特性,进一步提升代码的灵活性和通用性。

posted @ 2015-07-22 00:16  Mr.陳  阅读(250)  评论(0编辑  收藏  举报