iOS KVC (Key-Value Coding) 是一项十分强大的编程技术,能够让咱们以一种简略、灵敏的方式拜访和操作目标的特点。在本文中,咱们将深入探讨 iOS KVC 的完成原理和运用办法,并介绍如何运用 KVC 进行数据模型转化和目标联系映射。

完成原理

在 Objective-C 中,每个目标都有一个isa指针,指向其类目标的实例。KVC 机制运用isa指针、运行时(runtime)和音讯传递机制来完成特点的拜访和操作。

SetValue:forKey:

官方文档

Search Pattern for the Basic Setter

The default implementation ofsetValue:forKey:, givenkeyandvalueparameters as input, attempts to set a property namedkeytovalue(or, for non-object properties, the unwrapped version ofvalue, as described inRepresenting Non-Object Values) inside the object receiving the call, using the following procedure:

  1. Look for the first accessor namedset<Key>:or_set<Key>, in that order. If found, invoke it with the input value (or unwrapped value, as needed) and finish.
  2. If no simple accessor is found, and if the class methodaccessInstanceVariablesDirectlyreturnsYES, look for an instance variable with a name like_<key>,_is<Key>,<key>, oris<Key>, in that order. If found, set the variable directly with the input value (or unwrapped value) and finish.
  3. Upon finding no accessor or instance variable, invokesetValue:forUndefinedKey:. This raises an exception by default, but a subclass ofNSObjectmay provide key-specific behavior.

调用过程

  1. 首先依照set<Key>、 _set<Key>的次序查找办法,假如找到了就调用并赋值,完成KVC流程
  2. 假如没找到以上两个办法,而且类办法accessInstanceVariablesDirectly回来 YES,则依照_<Key>、_is<Key>、<Key>、is<Key>的次序查找,假如找到其间一个就将传入的值赋值,并完成KVC流程
  3. 假如以上两步都没有找到,就调用setValue:forUndefinedKey:办法,并抛出反常

流程图

通过iOS KVC模式打破传统编码壁垒

ValueForKey:

官方文档

Search Pattern for the Basic Getter

The default implementation ofvalueForKey:, given akeyparameter as input, carries out the following procedure, operating from within the class instance receiving thevalueForKey:call.

  1. Search the instance for the first accessor method found with a name likeget<Key>,<key>,is<Key>, or_<key>, in that order. If found, invoke it and proceed to step 5 with the result. Otherwise proceed to the next step.

  2. If no simple accessor method is found, search the instance for methods whose names match the patternscountOf<Key>andobjectIn<Key>AtIndex:(corresponding to the primitive methods defined by theNSArrayclass) and<key>AtIndexes:(corresponding to theNSArraymethodobjectsAtIndexes:).

    If the first of these and at least one of the other two is found, create a collection proxy object that responds to allNSArraymethods and return that. Otherwise, proceed to step 3.

    The proxy object subsequently converts anyNSArraymessages it receives to some combination ofcountOf<Key>,objectIn<Key>AtIndex:, and<key>AtIndexes:messages to the key-value coding compliant object that created it. If the original object also implements an optional method with a name likeget<Key>:range:, the proxy object uses that as well, when appropriate. In effect, the proxy object working together with the key-value coding compliant object allows the underlying property to behave as if it were anNSArray, even if it is not.

  3. If no simple accessor method or group of array access methods is found, look for a triple of methods namedcountOf<Key>,enumeratorOf<Key>, andmemberOf<Key>:(corresponding to the primitive methods defined by theNSSetclass).

    If all three methods are found, create a collection proxy object that responds to allNSSetmethods and return that. Otherwise, proceed to step 4.

    This proxy object subsequently converts anyNSSetmessage it receives into some combination ofcountOf<Key>,enumeratorOf<Key>, andmemberOf<Key>:messages to the object that created it. In effect, the proxy object working together with the key-value coding compliant object allows the underlying property to behave as if it were anNSSet, even if it is not.

  4. If no simple accessor method or group of collection access methods is found, and if the receiver’s class methodaccessInstanceVariablesDirectlyreturnsYES, search for an instance variable named_<key>,_is<Key>,<key>, oris<Key>, in that order. If found, directly obtain the value of the instance variable and proceed to step 5. Otherwise, proceed to step 6.

  5. If the retrieved property value is an object pointer, simply return the result.

    If the value is a scalar type supported byNSNumber, store it in anNSNumberinstance and return that.

    If the result is a scalar type not supported by NSNumber, convert to anNSValueobject and return that.

  6. If all else fails, invokevalueForUndefinedKey:. This raises an exception by default, but a subclass ofNSObjectmay provide key-specific behavior.

调用过程

  1. 依照get<Key>、<key>、is<Key>、_<key>的次序在实例目标中查找是否有已完成的办法,假如找到就调用,并执行第5步,不然继续下一步
  2. 假如上一步中没有找到,而且是一个数组类型,则调用数组相关的办法 如countOf<Key>、objectIn<Key>AtIndex:、<Key>AtIndex
  3. 假如上一步没找到,而且是一个调集类型,则调用调集相关办法如countOf<Key>、enumeratorOf<Key>、memberOf<Key>
  4. 假如以上都没找到,则判断accessInstanceVariablesDirectly是否为YES,假如为YES,则顺次查找成员变量_<key>,_is<Key>,<key>,is<Key>,假如查找到则进行第5步,不然进行第6步
  5. 查找到值,需要对值进行处理
  • 假如检索到的值是一个目标指针,则直接回来该目标
  • 假如是一个 NSNumber标量,则将其存储在NSNumber中并回来
  • 假如不是一个 NSNumber标量,则存储在 NSValue中并回来
  1. 在1~4未找到的情况下,抛出反常

流程图

通过iOS KVC模式打破传统编码壁垒

iOS KVC 的运用办法

iOS KVC 的运用十分简略,只需遵循以下规则:

  1. 特点名有必要是字符串类型,能够直接运用特点名作为字符串,也能够运用通过 NSString 类的实例办法创立的字符串。
  2. getter 办法有必要以 get 最初,而且将特点名的首字母大写。
  3. setter 办法有必要以 set 最初,而且将特点名的首字母大写,同时要接纳一个参数,即要设置的特点值。

下面是一个运用 KVC 拜访和设置目标特点的示例:

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@implementation Person
@end
// 创立 Person 目标
Person *person = [[Person alloc] init];
// 运用 KVC 拜访特点
NSString *name = [person valueForKey:@"name"];
NSInteger age = [[person valueForKey:@"age"] integerValue];
// 运用 KVC 设置特点
[person setValue:@"Tom" forKey:@"name"];
[person setValue:@30 forKey:@"age"];
​

在上面的示例中,咱们首先界说了一个名为 Person 的类,并界说了两个特点:name 和 age。然后咱们创立了一个 Person 目标,并运用 KVC 机制拜访和设置了其特点。

运用 KVC 进行数据模型转化

KVC 还能够用于将一个目标转化为另一个目标,这在数据模型转化时十分有用。例如,咱们能够运用 KVC 将 NSDictionary 转化为自界说的数据模型目标:

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@implementation Person
@end
// 创立 NSDictionary 目标
NSDictionary *dict = @{@"name": @"Tom", @"age": @30};
// 运用 KVC 将 NSDictionary 转化为 Person 目标
Person *person = [[Person alloc] init];
[person setValuesForKeysWithDictionary:dict];
​

在上面的示例中,咱们首先界说了一个名为 Person 的类,并界说了两个特点:name 和 age。然后咱们创立了一个 NSDictionary 目标,并运用 KVC 机制将其转化为 Person 目标。

运用 KVC 进行目标联系映射

KVC 还能够用于在目标之间树立相相联系,这在目标联系映射 (ORM) 中十分有用。例如,咱们能够运用 KVC 将两个目标之间树立起相相联系:

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@implementation Person
@end
@interface Car : NSObject
@property (nonatomic, copy) NSString *brand;
@property (nonatomic, strong) Person *owner;
@end
@implementation Car
@end
// 创立 Person 目标和 Car 目标
Person *person = [[Person alloc] init];
person.name = @"Tom";
Car *car = [[Car alloc] init];
car.brand = @"BMW";
// 运用 KVC 树立 Car 和 Person 之间的相相联系
[car setValue:person forKey:@"owner"];
​

在上面的示例中,咱们首先界说了一个名为 Person 和 Car 的类,并别离界说了它们的特点。然后咱们创立了一个 Person 目标和一个 Car 目标,并运用 KVC 机制将它们之间树立起相相联系。

总结

iOS KVC 是 Objective-C 中十分重要的编程技术,它能够帮助咱们以一种简略、灵敏的方式拜访和操作目标的特点,而且支持数据模型转化和目标联系映射等高档功用。在运用 KVC 时,咱们需要了解其完成原理和运用办法,以保证应用程序的功能和数据安全

参考文献

苹果官方文档 – 键值编码篇