Objective-C 的 KVC(二):NSKeyValueCoding.h 代码注释

/*
NSKeyValueCoding.h
Copyright (c) 1994-2019, Apple Inc. All rights reserved.
*/

#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSOrderedSet.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSException.h>

@class NSError, NSString;

NS_ASSUME_NONNULL_BEGIN

/* 
当 KVC 操作失败时抛出的异常。该异常的用户信息字典中将至少包含 2 个条目: 
	1. @"NSTargetObjectUserInfoKey":当前操作失败的 KVC 方法的调用者
	2. @"NSUnknownUserInfoKey":当前操作失败的 KVC 方法使用的 key
此字符串常量的实际值为 "NSUnknownKeyException," 用于与 MacOS 10.3 中弃用的 KVC 方法抛出的异常相匹配
*/
FOUNDATION_EXPORT NSExceptionName const NSUndefinedKeyException;

typedef NSString * NSKeyValueOperator NS_STRING_ENUM;

/* 
KVC 支持的集合运算符的名称
这些字符串是在 MacOS 10.4 中新声明的
对集合运算符的实际支持出现在 MacOS 10.3 中
注意:集合运算符的名称不包括 "@" 前缀
*/
FOUNDATION_EXPORT NSKeyValueOperator const NSAverageKeyValueOperator;
FOUNDATION_EXPORT NSKeyValueOperator const NSCountKeyValueOperator;
FOUNDATION_EXPORT NSKeyValueOperator const NSDistinctUnionOfArraysKeyValueOperator;
FOUNDATION_EXPORT NSKeyValueOperator const NSDistinctUnionOfObjectsKeyValueOperator;
FOUNDATION_EXPORT NSKeyValueOperator const NSDistinctUnionOfSetsKeyValueOperator;
FOUNDATION_EXPORT NSKeyValueOperator const NSMaximumKeyValueOperator;
FOUNDATION_EXPORT NSKeyValueOperator const NSMinimumKeyValueOperator;
FOUNDATION_EXPORT NSKeyValueOperator const NSSumKeyValueOperator;
FOUNDATION_EXPORT NSKeyValueOperator const NSUnionOfArraysKeyValueOperator;
FOUNDATION_EXPORT NSKeyValueOperator const NSUnionOfObjectsKeyValueOperator;
FOUNDATION_EXPORT NSKeyValueOperator const NSUnionOfSetsKeyValueOperator;

@interface NSObject(NSKeyValueCoding)

/* 
方法调用者在调用以下方法时,如果可以直接操作方法调用者的成员变量,则返回 YES。否则,返回 NO。该方法默认返回 YES
-valueForKey:
-setValue:forKey:
-mutableArrayValueForKey:
-storedValueForKey:
-takeStoredValue:forKey:
-takeValue:forKey: 
*/
@property (class, readonly) BOOL accessInstanceVariablesDirectly;

/* 
获取方法调用者中给定 key 所标识的属性或者成员变量的值

此方法的默认实现会执行以下操作:
1. 
按照 get<Key>、<key>、is<Key>、(_get<Key>、_<key>)的顺序在方法调用者所属的类中查找 getter 方法。如果找到相应的 getter 方法,则调用之
如果 getter 方法的返回值类型是对象指针类型,则直接返回结果
如果 getter 方法的返回值类型是 NSNumber 支持的标量类型之一,则将返回值转换成 NSNumber 类型的对象并返回 
否则,将返回值转换成 NSValue 类型的对象并返回(在 MacOS 10.5 中:任意类型的结果将转换为 NSValues,而不仅仅是 NSPoint、NRange、NSRect 和 NSSize)

2.
(在 MacOS 10.7 中引入)(没有找到简单的访问器方法)
在方法调用者所属的类中查找
-countOf<Key>
-indexIn<Key>OfObject:(对应于 -[NSOrderedSet indexOfObject:])
-objectIn<Key>AtIndex:(对应于 -[NSOrderedSet objectAtIndex:])
-<key>AtIndexes:(对应于 -[NSOrderedSet objectsAtIndexes:])
如果找到一个 count 方法和一个 indexOf 方法,以及另外两个可能的方法中的至少一个,则返回响应所有 NSOrderedSet 方法的集合代理对象(集合代理对象 NSKeyValueOrderedSet 为 NSOrderedSet 的子类)
发送到集合代理对象的每个 NSOrdereredSet 消息将会被转换成方法调用者所属的类中以下方法的某些组合
-countOf<Key>、-indexIn<Key>OfObject:、-objectIn<Key>AtIndex:、-<key>AtIndexes:
如果在方法调用者所属的类中还实现了一个名称为 -get<Key>:range: 的可选方法,则该可选方法将在适当的时候被调用以获得最佳性能

3.
(如果没有找到简单的访问器方法,没有找到 NSOrderedSet 的相关访问方法)
在方法调用者所属的类中查找
-countOf<Key>
-objectIn<Key>AtIndex:(对应于 -[NSArray objectAtIndex:])
-<key>AtIndexes:(对应于 -[NSArray objectsAtIndexes:])(在 MacOS 10.4 中引入)
如果找到一个 count 方法和另外两个可能方法中的一个,则返回响应所有 NSArray 方法的集合代理对象(集合代理对象 NSKeyValueArray 为 NSArray 的子类)
发送到集合代理对象的每个 NSArray 消息将会被转换成方法调用者所属的类中以下方法的某些组合:
-countOf<Key>、-objectIn<Key>AtIndex:、-<key>AtIndexes:
如果在方法调用者所属的类中还实现了一个名称为 -get<Key>:range: 的可选方法,则该可选方法将在适当的时候被调用以获得最佳性能

4.
(在 MacOS 10.4 中引入)(没有找到简单的访问器方法,没有找到 NSOrderedSet 的相关访问方法,没有找到 NSArray 的相关访问方法)
在方法调用者所属的类中查找
-countOf<Key>
-enumeratorOf<Key>
-memberOf<Key>:(对应于 -[NSSet member])
如果找到所有这三个方法,则将返回响应所有 NSSet 方法的集合代理对象(集合代理对象 NSKeyValueSet 为 NSSet 的子类)
发送到集合代理对象的每个 NSSet 消息将会被转换成方法调用者所属的类中以下方法的某些组合:
-countOf<Key>、-enumeratorOf<Key>、-memberOf<Key>:

5.
(没有找到简单的访问器方法,没有找到 NSOrderedSet 的相关访问方法,没有找到 NSArray 的相关访问方法,没有找到 NSSet 的相关访问方法)
如果方法调用者的类属性 +accessInstanceVariablesDirectly 返回 YES
则按照 _<key>、_is<Key>、<key>、is<Key> 的顺序在方法调用者中查找成员变量
如果找到这样的成员变量,则返回方法调用者中该成员变量的值,并且与步骤 1 一样,查看是否需要转换成 NSNumber 或 NSValue 类型的对象

6. 
(没有找到简单的访问器方法,没有找到 NSOrderedSet 的相关访问方法,没有找到 NSArray 的相关访问方法,没有找到 NSSet 的相关访问方法,没有找到相关的成员变量)
调用 -valueForUndefinedKey: 并返回调用结果。-valueForUndefinedKey: 的默认实现是抛出异常 NSUnknownKeyException,并导致程序 Crash。开发者可以重写该方法根据特定的 key 做一些特殊处理

兼容性说明:
	- 为了向后的兼容性,会在步骤 1 和步骤 3 之间搜索 getter 方法 -_get<Key> 和 -_<key>。如果找到了这样的方法,则调用与步骤 1 相同的 NSNumber 或 NSValue 的转换过程。不过,在 MacOS 10.3 中,其名称以下划线开头的 KVC 访问器方法已被弃用
	- 在步骤 5 中描述的行为是来自 MacOS 10.2 中的变化,在 MacOS 10.2 中实例变量搜索顺序为 <key> 、 _<key>
	- 为了向后的兼容性,在步骤 6 中
	  如果方法调用者所属的类的 -handleQueryWithUnboundKey: 的实现不是由 NSObject 提供
	  即方法调用者所属的类的 -handleQueryWithUnboundKey: 被子类重写了
	  则将会调用 -handleQueryWithUnboundKey 而不是 -valueForUndefinedKey: 
*/
- (nullable id)valueForKey:(NSString *)key;

/* 
将方法调用者中给定 key 所标识的属性或者成员变量的值设置为给定的 value

此方法的默认实现会执行以下操作:
1. 
按照 set<Key>:(_set<Key>:、setIs<Key>)的顺序在方法调用者所属的类中查找 setter 方法
如果找到相应的 setter 方法,则检查该 setter 方法的参数类型
如果该 setter 方法的参数类型不是对象指针类型,但是给定的 value 却为 nil,则调用方法调用者的 -setNilValueForKey:,-setNilValueForKey: 的默认实现是抛出异常 NSInvalidArgumentException,并导致程序 Crash。开发者可以重写该方法根据特定的 key-value 做一些特殊处理
如果该 setter 方法的参数类型是对象指针类型,则用给定的 value 作为参数简单地调用该 setter 方法
如果该 setter 方法的参数类型是基础数据类型或者结构体类型,则在调用该 setter 方法之前先对给定的 value 执行 NSNumber/NSValue 的拆箱操作

2. 
(没有找到简单的访问器方法)
如果方法调用者的类属性 +accessInstanceVariablesDirectly 返回 YES
则按照 _<key>、_is<Key>、<key>、is<Key> 的顺序在方法调用者中查找成员变量
如果找到了这样的成员变量,并且该成员变量是对象指针类型,则首先 release 该成员变量,其次 retain 给定的 value,最后将给定的 value 赋值给该成员变量
如果找到了这样的成员变量,并且该成员变量是基本数据类型或者结构体类型,则首先对给定的 value 执行与步骤 1 相同的 NSNumber/NSValue 的拆箱操作,然后再把从给定的 value 中提取到的数据赋值给该成员变量

3.
(没有找到简单的访问器方法,没有找到相关的成员变量)
调用 -setValue:forUndefinedKey:,-setValue:forUndefinedKey: 的默认实现是抛出异常 NSUndefinedKeyException,并导致程序 Crash
开发者可以重写该方法根据特定的 key-value 做一些特殊处理

兼容性说明:
	- 为了向后兼容 -takeValue:forKey: 方法,同样会在步骤 1 中识别名为 -_set<Key>: 的 setter 方法。不过,在 MacOS 10.3 中,其名称以下划线开头的 KVC 访问器方法已被弃用
	- 为了向后兼容,在步骤 1 中
	  如果方法调用者所属的类的 -unableToSetNilForKey: 的实现不是由 NSObject 提供
	  即方法调用者所属的类的 -unableToSetNilForKey: 被子类重写了
	  则将会调用 -unableToSetNilForKey: 而不是 -setNilValueForKey: 
	- 此方法在步骤 2 中描述的行为不同于 -takeValue:forKey:,在 -takeValue:forKey: 中成员变量的搜索顺序为 <key>、_<key>
	- 为了向后兼容 -takeValue:forKey: 方法,在步骤 3 中
	  如果方法调用者所属的类的 -handleTakeValue:forUnboundKey: 的实现不是由 NSObject 提供
	  即方法调用者所属的类的 -handleTakeValue:forUnboundKey: 被子类重写了
	  则将会调用 -handleTakeValue:forUnboundKey: 而不是 -setValue:forUndefinedKey:
*/
- (void)setValue:(nullable id)value forKey:(NSString *)key;

/* 
验证要设置给属性或者成员变的值的合法性
@param.ioValue 指向要设置给属性或者成员变的值的指针,当此方法调用完成之后,会得到一个适合于后续 -setValue:forKey: 使用的值
@param.inKey 用于标识要验证的属性或者成员变量的 key
@param.outError 一个指向 NSError 类型的对象的指针
@return 如果不需要进行验证,则直接返回 YES。注意:不要修改 *ioValue 和 *outError
		如果需要进行验证并且可以执行验证,则将(作为原始值的已验证版本的对象)赋值给 *ioValue,然后返回 YES。注意:不要修改 *outError
		如果需要进行验证但又无法执行验证,则将无法执行验证的原因封装在一个 NSError 对象中并赋值给 *outError,然后返回 NO。注意:不要修改 *ioValue
@note 此方法的调用者不会负责释放 ioValue 和 outError
@note 此方法的默认实现会在方法调用者所属的类中搜索名称为 -validate<Key>:error: 的验证方法
	  如果找到相应的验证方法,则调用该验证方法并返回该验证方法的执行结果
	  如果没有找到相应的验证方法,则返回 YES
@note 此方法需要开发者手动调用,KVC 在进行赋值时不会主动调动此方法验证属性值的合法性
	  即使开发者在方法调用者所属的类中重写了此方法,但是因为 KVC 不会主动调用此方法验证属性值的合法性,所以即使是非法值也还是能赋值成功
*/
- (BOOL)validateValue:(inout id _Nullable * _Nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

/* 
给定一个用于标识有序的一对多关系的 key,返回一个用于存储相关对象的可读可写的可变数组对象
添加到可变数组中的对象将变得与此方法的调用者相关
从可变数组中移除的对象将变得与此方法的调用者无关

此方法的默认实现可以识别与 -valueForKey: 相同的简单访问器方法和数组访问器方法,并与 -valueForKey: 遵守相同的直接访问实例变量的策略
但是此方法总是会返回一个可变的集合代理对象,而不是像 -valueForKey: 那样返回不可变的集合。此方法同样会:
1.
在方法调用者所属的类中查找
	-insertObject:in<Key>AtIndex:(对应于 -[NSMutableArray insertObject:atIndex:])
	-removeObjectFrom<Key>AtIndex:(对应于 -[NSMutableArray removeObjectAtIndex:])
	-insert<Key>:atIndexes:(在 MacOS 10.4 中引入)(对应于 -[NSMutableArray insertObjects:atIndexes:])
	-remove<Key>AtIndexes:(在 MacOS 10.4 中引入)(对应于 -[NSMutableArray removeObjectsAtIndexes:])
如果至少找到一个 insert 方法和至少找到一个 remove 方法,则返回能响应所有 NSMutableArray 方法的集合代理对象,发送到集合代理对象的每个 NSMutableArray 消息都将会产生对方法调用者所属的类中以下方法组合的调用
	-insertObject:in<Key>AtIndex:
	-removeObjectFrom<Key>AtIndex:
	-insert<Key>:atIndexes:
	-remove<Key>AtIndexes:
如果在方法调用者所属的类中还实现了一个名称为 
	-replaceObjectIn<Key>AtIndex:withObject: 或者 
	-replace<Key>AtIndexes:with<Key>:(在 MacOS 10.4 中引入)
的可选方法,则该可选方法将在适当的时候被调用以获得最佳性能

2.
(没有找到与 NSMutableArray 相关的方法)
在方法调用者所属的类中查找 setter 方法 -set<Key>:
如果找到了此 setter 方法,则发送到集合代理对象的每个 NSMutableArray 消息都将会产生对方法调用者所属的类中以下方法的调用
	-set<Key>:

3.
(没有找到与 NSMutableArray 相关的方法,没有找到简单的 setter 方法)
如果方法调用者的类属性 +accessInstanceVariablesDirectly 返回 YES
则按照 _<key>、<key> 的顺序在方法调用者所属的类中查找成员变量
如果找到了相应的成员变量,则发送到集合代理对象的每个 NSMutableArray 消息将转发给相应的成员变量
因此,该成员变量的类型通常必须是 NSMutableArray 或者 NSMutableArray 的子类

4.
(没有找到与 NSMutableArray 相关的方法,没有找到简单的 setter 方法,没有找到相关的成员变量)
仍将返回一个可变的集合代理对象。发送到集合代理对象的每个 NSMutableArray 消息将会产生对方法调用者所属的类中以下方法的调用	
	-setValue:forUndefinedKey:
-setValue:forUndefinedKey: 的默认实现是抛出异常 NSUnknownKeyException,并导致程序 Crash。开发者可以重写该方法根据特定的 key-value 做一些特殊处理

性能说明:
	- 在步骤 2 中重复发送 -set<Key>: 消息是一个潜在的性能问题
	- 为了获得更好的性能,请在方法调用者所属的类中实现满足步骤 1 要求的 insert 方法和 remove 方法
	- 为了获得更好的性能,也可以为 -set<Key>: 实现一个替换方法
*/
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

/* 
给定一个用于标识有序且唯一的一对多关系的 key,返回一个用于存储相关对象的可读可写的 NSMutableOrderedSet 对象
添加到 NSMutableOrderedSet 中的对象将变得与此方法的调用者相关
从 NSMutableOrderedSet 中移除的对象将变得与此方法的调用者无关

此方法的默认实现可以识别与 -valueForKey: 相同的简单访问器方法和 OrderedSet 访问器方法,并与 -valueForKey: 遵守相同的直接访问实例变量的策略
但是此方法总是会返回一个可变的集合代理对象,而不是像 -valueForKey: 那样返回不可变的集合。此方法同样会:
1.
在方法调用者所属的类中查找
	-insertObject:in<Key>AtIndex:(对应于 -[NSMutableOrderedSet insertObject:atIndex:])
	-removeObjectFrom<Key>AtIndex:(对应于 -[NSMutableOrderedSet removeObjectAtIndex:])
	-insert<Key>:atIndexes:(对应于 -[NSMutableOrderedSet insertObjects:atIndexes:])
	-remove<Key>AtIndexes:(对应于 -[NSMutableOrderedSet removeObjectsAtIndexes:])
如果至少找到一个 insert 方法和至少找到一个 remove 方法,则返回能响应所有 NSMutableOrderedSet 方法的集合代理对象,发送到集合代理对象的每个 NSMutableOrderedSet 消息都将会产生对方法调用者所属的类中以下方法组合的调用
	-insertObject:in<Key>AtIndex:
	-removeObjectFrom<Key>AtIndex:
	-insert<Key>:atIndexes:
	-remove<Key>AtIndexes:
如果在方法调用者所属的类中还实现了一个名称为 
	-replaceObjectIn<Key>AtIndex:withObject: 或者 
	-replace<Key>AtIndexes:with<Key>:
的可选方法,则该可选方法将在适当的时候被调用以获得最佳性能

2.
(没有找到与 NSMutableOrderedSet 相关的方法)
在方法调用者所属的类中查找 setter 方法 -set<Key>:
如果找到了此 setter 方法,则发送到集合代理对象的每个 NSMutableOrderedSet 消息都将会产生对方法调用者所属的类中以下方法的调用
	-set<Key>:

3.
(没有找到与 NSMutableOrderedSet 相关的方法,没有找到简单的 setter 方法)
如果方法调用者的类属性 +accessInstanceVariablesDirectly 返回 YES
则按照 _<key>、<key> 的顺序在方法调用者所属的类中查找成员变量
如果找到了相应的成员变量,则发送到集合代理对象的每个 NSMutableOrderedSet 消息将转发给相应的成员变量
因此,该成员变量的类型通常必须是 NSMutableOrderedSet 或者 NSMutableOrderedSet 的子类

4.
(没有找到与 NSMutableOrderedSet 相关的方法,没有找到简单的 setter 方法,没有找到相关的成员变量)
仍将返回一个可变的集合代理对象。发送到集合代理对象的每个 NSMutableOrderedSet 消息将会产生对方法调用者所属的类中以下方法的调用	
	-setValue:forUndefinedKey:
-setValue:forUndefinedKey: 的默认实现是抛出异常 NSUnknownKeyException,并导致程序 Crash。开发者可以重写该方法根据特定的 key-value 做一些特殊处理

性能说明:
	- 在步骤 2 中重复发送 -set<Key>: 消息是一个潜在的性能问题
	- 为了获得更好的性能,请在方法调用者所属的类中实现满足步骤 1 要求的 insert 方法和 remove 方法
	- 为了获得更好的性能,也可以为 -set<Key>: 实现一个替换方法
*/
- (NSMutableOrderedSet *)mutableOrderedSetValueForKey:(NSString *)key API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));

/* 
给定一个用于标识无序且唯一的一对多关系的 key,返回一个用于存储相关对象的可读可写的 NSMutableSet 对象
添加到 NSMutableSet 中的对象将变得与此方法的调用者相关
从 NSMutableSet 中移除的对象将变得与此方法的调用者无关

此方法的默认实现可以识别与 -valueForKey: 相同的简单访问器方法和 Set(集合) 访问器方法,并与 -valueForKey: 遵守相同的直接访问实例变量的策略
但是此方法总是会返回一个可变的集合代理对象,而不是像 -valueForKey: 那样返回不可变的集合。此方法同样会:
1.
在方法调用者所属的类中查找
	-add<Key>Object:(对应于 -[NSMutableSet addObject:])
	-remove<Key>Object:(对应于 -[NSMutableSet removeObject:])
	-add<Key>(对应于 -[NSMutableSet unionSet:])
	-remove<Key>:(对应于 -[NSMutableSet minusSet:])
如果至少找到一个 add 方法和至少找到一个 remove 方法,则返回能响应所有 NSMutableSet 方法的集合代理对象,发送到集合代理对象的每个 NSMutableSet 消息都将会产生对方法调用者所属的类中以下方法组合的调用
	-add<Key>Object:
	-remove<Key>Object:
	-add<Key>:
	-remove<Key>:
如果在方法调用者所属的类中还实现了一个名称为 
	-intersect<Key>: 或者 
	-set<Key>:
的可选方法,则该可选方法将在适当的时候被调用以获得最佳性能

2.
(没有找到与 NSMutableSet 相关的方法)
在方法调用者所属的类中查找 setter 方法 -set<Key>:
如果找到了此 setter 方法,则发送到集合代理对象的每个 NSMutableSet 消息都将会产生对方法调用者所属的类中以下方法的调用
	-set<Key>:

3.
(没有找到与 NSMutableSet 相关的方法,没有找到简单的 setter 方法)
如果方法调用者的类属性 +accessInstanceVariablesDirectly 返回 YES
则按照 _<key>、<key> 的顺序在方法调用者所属的类中查找成员变量
如果找到了相应的成员变量,则发送到集合代理对象的每个 NSMutableSet 消息将转发给相应的成员变量
因此,该成员变量的类型通常必须是 NSMutableSet 或者 NSMutableSet 的子类

4.
(没有找到与 NSMutableSet 相关的方法,没有找到简单的 setter 方法,没有找到相关的成员变量)
仍将返回一个可变的集合代理对象。发送到集合代理对象的每个 NSMutableSet 消息将会产生对方法调用者所属的类中以下方法的调用	
	-setValue:forUndefinedKey:
-setValue:forUndefinedKey: 的默认实现是抛出异常 NSUnknownKeyException,并导致程序 Crash。开发者可以重写该方法根据特定的 key-value 做一些特殊处理

性能说明:
	- 在步骤 2 中重复发送 -set<Key>: 消息是一个潜在的性能问题
	- 为了获得更好的性能,请在方法调用者所属的类中实现满足步骤 1 要求的 add 方法和 remove 方法
	- 为了获得更好的性能,也可以为 -set<Key>: 实现一个替换方法
*/
- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;

/* 
在 KVC 中,每个使用 keyPath 作为参数的方法都有与之对应的使用 key 作为参数的方法

每段 keyPath 的解析都会确定剩余的 keyPath 是否还含有多个组件(keyPath 的组件以 . 进行分割)
如果剩余的 keyPath 还含有多个组件,则取出剩余 keyPath 中的第一个组件作为调用 -valueForKey: 的参数
并且被调用的方法会对结果进行递归调用,在递归调用的过程中会将剩余的 keyPath 作为参数传递
如果剩余的 keyPath 只剩下一个组件了,则会用剩下的该组件调用对应的使用 key 作为参数的方法

这里苹果官方的注释有点抽象,我举个例子
// 银行账号 account 中有一个 Person 类型的 owner 属性
// 账号拥有者 owner 中有一个 Address 类型的 addr 属性
// 居住地址 addr 中有一个 City 类型的 city 属性
// 城市 city 中有一个 NSString 类型的 street 属性
[account setValue:@"Zhongshan Road" forKeyPath:@"owner.addr.city.street"];

第 1 次调用:
剩余的 keyPath = @"owner.addr.city.street",剩余组件数 > 1
取出剩余 keyPath 中的第一个组件 @"owner"
调用 Person* aPerson = [account valueForKey:@"owner"];
调用 [aPerson setValue:@"Zhongshan Road" forKeyPath:@"addr.city.street"];

第 2 次调用:
剩余的 keyPath = @"addr.city.street",剩余组件数 > 1
取出剩余 keyPath 中的第一个组件 @"addr"
调用 Address* anAddress = [aPerson valueForKey:@"addr"];
调用 [anAddress setValue:@"Zhongshan Road" forKeyPath:@"city.street"];

第 3 次调用:
剩余的 keyPath = @"city.street",剩余组件数 > 1
取出剩余 keyPath 中的第一个组件 @"city"
调用 City* aCity = [anAddress valueForKey:@"city"];
调用 [aCity setValue:@"Zhongshan Road" forKeyPath:@"street"];

第 4 次调用:
剩余的 keyPath = @"street",剩余组件数 = 1
因为是调用 KVC 进行赋值
即调用的是 setValue:forKeyPath:
所以调用对应的 setValue:forKey:
即调用 [aCity setValue:@"Zhongshan Road" ForKey:@"street"];
*/
- (nullable id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
- (BOOL)validateValue:(inout id _Nullable * _Nonnull)ioValue forKeyPath:(NSString *)inKeyPath error:(out NSError **)outError;
- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;
- (NSMutableOrderedSet *)mutableOrderedSetValueForKeyPath:(NSString *)keyPath API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;

/* 
在调用 -valueForKey: 时
如果使用其默认的访问机制无法通过给定的 key 获取到对应的 value,则通过其他的机制获取给定的 key 对应的 value
此方法的默认实现会引发一个 NSUndefinedKeyException 异常
可以重写此方法以处理在运行时动态定义的属性
*/
- (nullable id)valueForUndefinedKey:(NSString *)key;

/* 
在调用 -setValue:forKey: 时
如果使用其默认的访问机制无法通过给定的 key 设置到对应的 value,则通过其他的机制设置给定的 key 对应的 value
此方法的默认实现会引发一个 NSUndefinedKeyException 异常
可以重写此方法以处理在运行时动态定义的属性
*/
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

/* 
在调用 -setValue:forKey: 时
如果给定的 key 对应的 setter 方法的参数类型是 NSNumber 指示的标量类型或者 NSValue 指示的结构体类型,但是给定的 value 的值却为 nil
则通过其他的机制设置给定的 key 对应的 value
此方法的默认实现会引发一个 NSInvalidArgumentException 异常
可以重写此方法用于将 nil 值映射成在程序上下文中有意义的值
*/
- (void)setNilValueForKey:(NSString *)key;

/* 
模型转字典
给定一组 key,获取方法调用者中该组 key 对应的 value,并将获取到的 key-value 封装成字典返回
如果通过 -valueForKey: 获取到的 value 是 nil,则会先将获取到的 nil 值装箱成 NSNull 对象,然后再添加到要返回的字典中
*/
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;

/* 
字典转模型
给定一个字典,获取字典中的 key-value,并设置方法调用者中该 key 对应的 value
如果字典中 key 对应的 value 为 NSNull 对象,则会先将获取到的 NSNull 对象拆箱成 nil,然后再赋值给对应的 value(-setValue:nil forKey:key)
*/
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;

@end

@interface NSArray<ObjectType>(NSKeyValueCoding)

/* 
返回一个 NSArray 对象,该 NSArray 对象包含:在方法调用者的每个元素上调用 -valueForKey: 的结果
注意:
1.此时的方法调用者是 NSArray 或者 NSMutableArray 类型
2.如果通过 [element valueForKey:key] 获取到的 value 是 nil,则会先将获取到的 nil 值装箱成 NSNull,然后再添加到要返回的 NSArray 对象中
3.数组对象(NSArray、NSMutableArray)调用 -valueForKey:
  实际上是对数组对象(NSArray、NSMutableArray)中的每一个元素调用 -valueForKey:
*/
- (id)valueForKey:(NSString *)key;

/*
在方法调用者的每个元素上调用 -setValue:forKey:
注意:
1.此时的方法调用者是 NSArray 或者 NSMutableArray 类型
2.数组对象(NSArray、NSMutableArray)调用 -setValue:forKey:
  实际上是对数组对象(NSArray、NSMutableArray)中的每一个元素调用 -setValue:forKey:
*/
- (void)setValue:(nullable id)value forKey:(NSString *)key;

@end

@interface NSDictionary<KeyType, ObjectType>(NSKeyValueCoding)

/* 
字典对象(NSDictionary、NSMutableDictionary)调用 -valueForKey:
实际上是调用字典对象(NSDictionary、NSMutableDictionary)的 -objectForKey:
*/
- (nullable ObjectType)valueForKey:(NSString *)key;

@end

@interface NSMutableDictionary<KeyType, ObjectType>(NSKeyValueCoding)

/* 
字典对象(NSMutableDictionary)调用 -setValue:forKey:
如果 value 不为 nil,则实际上调用的是字典对象(NSMutableDictionary)的 -setObject:forKey:
如果 value 为 nil,则实际上调用的是字典对象(NSMutableDictionary)的 -removeObjectForKey:
*/
- (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;

@end

@interface NSOrderedSet<ObjectType>(NSKeyValueCoding)

/* 
返回一个 NSOrderedSet 对象,该 NSOrderedSet 对象包含:在方法调用者的每个元素上调用 -valueForKey: 的结果
注意:
1.此时的方法调用者是 NSOrderedSet 或者 NSMutableOrderedSet 类型
2.返回的 NSOrderedSet 对象中所包含的元素的个数可能与方法调用者中所包含的元素的个数不同
3.返回的 NSOrderedSet 对象中不包含 nil,即 [element valueForKey:key] 获取的 nil 值将不会被添加到要返回的 NSOrderedSet 对象中
  而 [NSArray(NSKeyValueCoding) valueForKey:] 可能会将 NSNull 放入要返回的数组中
4.返回的 NSOrderedSet 对象中不包含重复的元素
5.集合对象(NSOrderedSet、NSMutableOrderedSet)调用 -valueForKey:
  实际上是对集合对象(NSOrderedSet、NSMutableOrderedSet)中的每一个元素调用 -valueForKey:
*/
- (id)valueForKey:(NSString *)key API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));

/* 
在方法调用者的每个元素上调用 -setValue:forKey:
注意:
1.此时的方法调用者是 NSOrderedSet 或者 NSMutableOrderedSet 类型
2.集合对象(NSOrderedSet、NSMutableOrderedSet)调用 -setValue:forKey:
  实际上是对集合对象(NSOrderedSet、NSMutableOrderedSet)中的每一个元素调用 -setValue:forKey:
*/
- (void)setValue:(nullable id)value forKey:(NSString *)key API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));

@end

@interface NSSet<ObjectType>(NSKeyValueCoding)

/* 
返回一个 NSSet 对象,该 NSSet 对象包含:在方法调用者的每个元素上调用 -valueForKey: 的结果
注意:
1.此时的方法调用者是 NSSet 或者 NSMutableSet 类型
2.返回的 NSSet 对象中所包含的元素的个数可能与方法调用者中所包含的元素的个数不同
3.返回的 NSSet 对象中不包含 nil,即 [element valueForKey:key] 获取的 nil 值将不会被添加到要返回的 NSSet 对象中
  而 [NSArray(NSKeyValueCoding) valueForKey:] 可能会将 NSNull 放入要返回的数组中
4.返回的 NSSet 对象中不包含重复的元素
5.集合对象(NSSet、NSMutableSet)调用 -valueForKey:
  实际上是对集合对象(NSSet、NSMutableSet)中的每一个元素调用 -valueForKey:
*/
- (id)valueForKey:(NSString *)key;

/* 
在方法调用者的每个元素上调用 -setValue:forKey:
注意:
1.此时的方法调用者是 NSSet 或者 NSMutableSet 类型
2.集合对象(NSSet、NSMutableSet)调用 -setValue:forKey:
  实际上是对集合对象(NSSet、NSMutableSet)中的每一个元素调用 -setValue:forKey:
*/
- (void)setValue:(nullable id)value forKey:(NSString *)key;

@end

#if TARGET_OS_OSX

@interface NSObject(NSDeprecatedKeyValueCoding)

/* 在 MacOS 10.4 中被弃用的方法。请使用上面声明的命名更一致的新方法 */
+ (BOOL)useStoredAccessor API_DEPRECATED("Legacy KVC API", macos(10.0,10.4), ios(2.0,2.0), watchos(2.0,2.0), tvos(9.0,9.0));
- (nullable id)storedValueForKey:(NSString *)key API_DEPRECATED("Legacy KVC API", macos(10.0,10.4), ios(2.0,2.0), watchos(2.0,2.0), tvos(9.0,9.0));
- (void)takeStoredValue:(nullable id)value forKey:(NSString *)key API_DEPRECATED("Legacy KVC API", macos(10.0,10.4), ios(2.0,2.0), watchos(2.0,2.0), tvos(9.0,9.0));

/* 在 MacOS 10.3 中被弃用的方法。请使用上面声明的命名更一致的新方法 */
- (void)takeValue:(nullable id)value forKey:(NSString *)key API_DEPRECATED("Legacy KVC API", macos(10.0,10.3), ios(2.0,2.0), watchos(2.0,2.0), tvos(9.0,9.0));
- (void)takeValue:(nullable id)value forKeyPath:(NSString *)keyPath API_DEPRECATED("Legacy KVC API", macos(10.0,10.3), ios(2.0,2.0), watchos(2.0,2.0), tvos(9.0,9.0));
- (nullable id)handleQueryWithUnboundKey:(NSString *)key API_DEPRECATED("Legacy KVC API", macos(10.0,10.3), ios(2.0,2.0), watchos(2.0,2.0), tvos(9.0,9.0));
- (void)handleTakeValue:(nullable id)value forUnboundKey:(NSString *)key API_DEPRECATED("Legacy KVC API", macos(10.0,10.3), ios(2.0,2.0), watchos(2.0,2.0), tvos(9.0,9.0));
- (void)unableToSetNilForKey:(NSString *)key API_DEPRECATED("Legacy KVC API", macos(10.0,10.3), ios(2.0,2.0), watchos(2.0,2.0), tvos(9.0,9.0));
- (NSDictionary *)valuesForKeys:(NSArray *)keys API_DEPRECATED("Legacy KVC API", macos(10.0,10.3), ios(2.0,2.0), watchos(2.0,2.0), tvos(9.0,9.0));
- (void)takeValuesFromDictionary:(NSDictionary *)properties API_DEPRECATED("Legacy KVC API", macos(10.0,10.3), ios(2.0,2.0), watchos(2.0,2.0), tvos(9.0,9.0));

@end

#endif

NS_ASSUME_NONNULL_END
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值