NSKeyValueCoding

NSKeyValueCoding

键值编码,简写为KVC。提供一种通过字符串来访问类中的属性和成员变量的方法,并可以借助运算符进行计算。

NSUndefinedKeyException

当键值编码操作失败时会引发NSUndefinedKeyException(值:NSUnknownKeyException)异常。

NSKeyValueOperator

typedef NSString * NSKeyValueOperator NS_TYPED_ENUM;
定义了NSKeyValueOperatorNSString *类型的枚举;

NSKeyValueOperator有如下几种类型:

  • NSAverageKeyValueOperator
    avg:平均值计算,比如:@avg.age

  • NSCountKeyValueOperator
    count:数量统计,比如:@count。除此之外的操作类型都需要一个右侧keypath。

  • NSDistinctUnionOfArraysKeyValueOperator
    distinctUnionOfArrays:获取嵌套数组中不同的值,结果合并到同一数组,比如:@distinctUnionOfArrays.age

  • NSDistinctUnionOfObjectsKeyValueOperator
    distinctUnionOfObjects:获取数组中不同的值,比如:@distinctUnionOfObjects.age

  • NSDistinctUnionOfSetsKeyValueOperator
    distinctUnionOfSets:获取嵌套集合(或元素为数组)中不同的值,结果合并到同一集合

  • NSMaximumKeyValueOperator
    max:distinctUnionOfObjects:获取最大值

  • NSMinimumKeyValueOperator
    min:获取最小值

  • NSSumKeyValueOperator
    sum:获取总数

  • NSUnionOfArraysKeyValueOperator
    unionOfArrays:获取嵌套数组中的值,结果合并到同一数组

  • NSUnionOfObjectsKeyValueOperator
    unionOfObjects:获取数组中的值

  • NSUnionOfSetsKeyValueOperator
    unionOfSets:获取嵌套集合(或元素为数组)中的值

Operator key path format

NSObject(NSKeyValueCoding)

@property (class, readonly) BOOL accessInstanceVariablesDirectly

如果-valueForKey:-setValue:forKey:-mutableArrayValueForKey:-storedValueForKey:-takeStoredValue:forKey:,和-takvalue:forKey:可以直接操作实例变量发送到接收类的实例,则返回YES,否则返回NO。此属性的默认实现返回YES。

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

给定一个键名,返回属性值或相关对象。
该方法的默认实现如下:

  1. 在当前类中搜索一个访问方法,方法的名称按后面的顺序匹配-get<Key>, -<key>, 或 -is<Key>。如果查询到匹配的方法就调用。

    • 如果返回的结果是对象指针类型则直接返回结果;
    • 如果是NSNumber支持的标量类型转换成NSNumber类型并返回;
    • 否则,转换完成一个NSValue并返回。
  2. 如果没有查询到匹配的方法,则继续按照后面的顺序匹配:-countOf<Key>-indexIn<Key>OfObject:-objectIn<Key>AtIndex:(对应NSOrderedSet定义的原始方法)和 -<key>AtIndexes:(对应-[NSOrderedSet objectsAtIndexes:])。如果匹配到了count方法和indexOf方法,以及至少另外两个可能的方法中的一个,则返回一个响应所有NSOrderedSet方法的集合代理对象。每个发送到集合代理对象的NSOrderedSet消息将导致-countOf<Key>-indexIn <Key>OfObject:-objectIn<Key>AtIndex:,和-<Key>AtIndexes:消息被发送到-valueForkey:的原始接收者。如果接收方的类还实现了一个名称匹配模式的可选方法-get<Key>:range:该方法将在适当的时候用于最佳性能。

  3. 否则,继续按照后面的顺序匹配:-countOf<Key>and-objectIn<Key>AtIndex:(对应NSArray定义的原始方法) and also -<key>AtIndexes:(对应-[NSArray objectsAtIndexes:])。如果找到一个count方法和另外两个可能的方法中的至少一个,则返回一个响应所有NSArray方法的集合代理对象。每个发送到集合代理对象的NSArray消息都将导致-countOf<Key>-objectIn<Key>AtIndex:和-AtIndexes:消息被发送到-valueForKey:的原始接收者。如果接收方的类还实现了一个名称匹配模式的可选方法-get<Key>:range:该方法将在适当的时候用于最佳性能。

  4. 否则,继续按照后面的顺序匹配:-countOf<Key>, -enumeratorOf<Key>, and -memberOf<Key>: (对应NSSet类的原始方法)。如果找到所有这三个方法,则返回一个响应所有NSSet方法的集合代理对象。发送到集合代理对象的每个NSSet消息将导致-countOf<Key>-enumeratorOf<Key>,和-memberOf<Key>:消息被发送到-valueForKey:的原始接收者。

  5. 否则,如果类中的类方法+accessinstancevariablesdirect返回YES,则在接收方的类中搜索一个实例变量,其名称按后面顺序匹配_<key>,_is<Key>, <key>,或is<Key>。如果找到这样的实例变量,则返回接收方中实例变量的值,与步骤1中相同的转换为NSNumberNSValue

  6. 否则,调用-valueForUndefinedKey:并返回结果。-valueForUndefinedKey: 的默认实现:引发一个NSUndefinedKeyException,但你可以在你的应用程序中重写覆盖原始实现。

兼容说明:

  • 为了向后二进制兼容性,在第1步和第3步之间搜索名称与模式-_get<Key>-_<key>匹配的访问器方法。如果找到这样的方法,则调用它,使用相同的类型转换为步骤1中的NSNumberNSValue。但名称以下划线开头的KVC访问器方法在 Mac OS 10.3 中已被弃用。

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

该方法的默认实现如下:

  1. 在当前类中搜寻-set<Key>:方法。如果找到了,则检查它的参数类型,如果参数类型不是对象指针类型,且值为nil,则调用-setNilValueForKey:-setNilValueForKey:的默认实现会引发一个NSInvalidArgumentException异常,但是你可以在你的应用程序中重写它。否则,如果方法的形参类型是对象指针类型,则只需将值作为参数调用该方法。如果方法参数的类型是其他类型,则在调用方法之前执行由 -valueForKey:完成的NSNumberNSValue转换的逆操作。
  2. 否则,如果接收者的类的+accessInstanceVariablesDirectly属性返回YES,则在接收者的类中按后面的顺序匹配变量名:_<key>_is<Key><key>is<Key>。如果找到这样的实例变量并且它的类型是对象指针类型,则在第一次释放实例变量的旧值之后,保留该值并将结果设置在实例变量中。 如果实例变量的类型是其他类型,则它的值是在与步骤 1 中从 NSNumber 或 NSValue 进行相同类型的转换之后设置的。
  3. 否则,调用-setValue:forUndefinedKey:-setValue:forUndefinedKey:的默认实现会引发NSUndefinedKeyException,但您可以在应用程序中覆盖它。

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

For Collection

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
获取到的可变数组,添加对象将影响接收者。删除对象不影响接收者。
- (NSMutableOrderedSet *)mutableOrderedSetValueForKey:(NSString *)key;
- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;

For KeyPath

- (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;

Other

可以通过运行时动态存取不存在的属性。
- (nullable id)valueForUndefinedKey:(NSString *)key;
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

当值为nil时会调用此方法,会引发一个异常。可以重写处理。
- (void)setNilValueForKey:(NSString *)key;

- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;

NSArray、NSOrderedSet、NSSet

集合中每个元素都会执行一次并返回集合结果
- (id)valueForKey:(NSString *)key;
集合中每个元素都会执行一次
- (void)setValue:(nullable id)value forKey:(NSString *)key;

NSDictionary、NSMutableDictionary


- (id)valueForKey:(NSString *)key; //相当于-objectForKey:

- (void)setValue:(nullable id)value forKey:(NSString *)key; //相当于-setObject:forKey: