KVC - Software Design
其实是一种基于 NSObject 查询对象属性的机制
现在我们从代码设计的角度来分析 KVC
1. Key & Value
例如类 Person 如下:
Class Person: NSObject {
var name: String = ""
var age: Int = 0
}
比如说 Person 对象有 name 属性,我们想通过某个 API 来获取,或者设置 name 的值,即:
set "ienos" For Name
get String For Name
抽象化该方法表示为(Key 为字符串):
setValue:forKey:
valueForKey:
那么我们就可以通过 Key 为 name,来寻找对应的 value,获取对应的属性值
或者通过 Key 来设置 value 的值,从而对属性进行赋值
另外为了防止我们对不存在的 Key 进行操作,这里引入验证 Key 是否有效的 API
validateValue:forKey:error:
2. Key Undefiend
如果 Key 不存在如何处理?
- 提示开发者 Key 不存在,抛异常
- 允许开发者自定义映射 Key
2.1 valueForUndefinedKey:
比如说客户端从云端请求到的 JSON 为 ["Name": "ienos"]
通过解析,我们需要将 JSON 中的 Name 映射到 Person 的 name
那么此时开发者需要可以自定义该映射表
因此开放 valueForUndefinedKey:
允许开发者重写该方法,自定义映射表
- (void)setValue:(id)value forKey:(NSString *)key {
if ([key isEqualToString:@"Name"]) {
key = @"name";
}
[super setValue:value forKey:key];
}
3. Value Type
在设计 Value 时,考虑到 Value 有可能为:对象类型
、基本数据类型
在 Objective-C 中,如果要同时满足 Value 为 对象类型
、基本数据类型
如何处理?
- Value 为 id 类型,对象保持原有的类
- 对于基本数据类型:int、float、char etc. 还有结构体 struct
- int、float、char etc 用 NSNumber 表示
- struct 用 NSValue 表示
那当 setValue 为 nil 时,需不需要处理?
-
setValue:ForKey
为 nil,此时对于基本数据类型不存在值则抛出异常。 - 针对这种情况需要提供开发者可重写方法,默认抛出异常
重写可为 Key 设置默认值 setNilValueForKey:
4. 多 Key 赋值,取值
如果我们需要对多个 Key 进行赋值怎么办? 或者说获取部分 Key 对应的值
setValuesForKeysWithDictionary:
dictionaryWithValuesForKeys:
5. KeyPath
当我们的类有多层结构,那么我们应该如何设计?
假如 Person 新增一个 company(Company类) 的属性
class Person: NSObject {
var name: String = ""
var age: Int = 0
var company: Company = Company.init()
}
class Company: NSObject {
var name: String = ""
}
那原有的 setValue:forKey
就不再适用,我们引入 keyPath 的概念
通过路径的方式访问 Key
setValue:forKeyPath:
valueForKeyPath:
let person = Person.init()
person.company.name = "Apple"
person.setValue("Apple", forKeyPath: "company.name")
person.valueForKeyPath("company.name")
keypath 通过寻址的过程,内部实现依旧调用 setValue:forKey
4. Collection Value
假如 Person 新增一个 family 数组属性
class Person: NSObject {
var name: String = ""
var age: Int = 0
var company: Company = Company.init()
var cardIdentifies: [String] = []
}
class Company: NSObject {
var name: String = ""
}
也就是说 Person 类中有一个 cardIdentifies 的属性,其中 cardIdentifies 是个数组且元素为字符串实例对象
valueForKey:
返回的是 id 类型
如果我使用 valueForKey("cardIdentifies")
还需要转类型转为 Array
对于经常操作 Array 的开发者不够友好
所以需要新增对集合元素的 API
// Array
- mutableArrayValueKey: / mutableArrayValueForKeyPath:
// Set
- mutableSetValueForKey: / mutableSetValueForKeyPath:
// OrderedSet
- mutableOrderedSetValueForKey: / mutableOrderedSetValueForKeyPath:
5. Collection Operators
为了方便集合类型的操作,引入 Collection Operators
Operator key path format例如:
- 计算平均值
transactions.value(forKeyPath: "@avg.amount")
- 计算总数
transactions.value(forKeyPath: "@count")
详细 API 见 [iOS - Key Value Coding]