文章目录
前言
提示:这里可以添加本文要记录的大概内容:
简单介绍
KVC(KeyValueCoding)俗称“键值编码,是指可以通过一个key来访问某个属性
在iOS中,提供了一种方法通过使用属性的名称(key)来间接访问属性对象的方法,这个方法可以不通过setter/getter方法来访问对象的属性
KVC提供了一种间接访问其属性或成员变量的机制,可以通过字符串来访问其对应的属性方法或者成员变量
KVC还可以访问私有变量。某些情况下,KVC还可以帮助简化代码
访问对象属性
常用API
//通过key来取值
- (id)valueForKey:(NSString *)key;
//通过keyPath来取值
- (id)valueForKeyPath:(NSString *)keyPath;
//通过key来赋值
- (void)setValue:(id)value forKey:(NSString *)key;
//通过keyPath来赋值
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
key和keyPath
key:只能接收当前类所具有的属性,不管是自己的还是从父类继承的
keyPath:除了能接受当前类的属性,还能接受当前类属性的属性。(可以接收关系链)
- (void)setValue:(id)value forKey:(NSString *)key;
// Dog.h
// KVC
//
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class Bone;
@interface Dog : NSObject{
@package
NSString* gender;
NSString* _gender;
}
@property(nonatomic, copy)NSString* name;
@property(nonatomic, strong)Bone* bone;
@property(nonatomic, assign)int weight;
@end
NS_ASSUME_NONNULL_END
//
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Bone : NSObject
@property (nonatomic, strong)NSString* type;
@property (nonatomic, assign)NSInteger* weight;
@end
NS_ASSUME_NONNULL_END
// main.m
// KVC
//
//
#import <Foundation/Foundation.h>
#import "Dog.h"
#import "Bone.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
Dog* dog = [[Dog alloc]init];
dog.bone = [[Bone alloc] init];
[dog setValue:@"huacheng" forKey:@"name"];
[dog.bone setValue:@"lanzhan" forKey:@"type"];
NSLog(@"%@",[dog valueForKey:@"name"]);
NSLog(@"%@",[dog.bone valueForKey:@"type"]);
}
return 0;
}
调用setValue: forKey:
- 优先调用set:方法。代码通过setter方法完成设置。
- 如果没有找到setName:方法,检查 + (BOOL)accessInstanceVariablesDirectly(默认返回YES)方法是否返回YES,如果设置返回为NO,执行- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key方法。
- 如果该类没有setKey:方法,也没有_成员变量,KVC会搜索_is< Key >的成员变量;
- 如果该类没有set:方法,也没有_和_is< key>的成员变量,KVC 机制会呢继续搜索和is< key>的成员变量。在进行赋值操作。
- 如果上面所述都不存在,执行- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;方法,默认抛出异常。
// Dog.h
// KVC
//
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
//@class Bone;
@interface Dog : NSObject{
NSString* toSetName;
NSString* isName;
NSString* _name;
NSString* _isName;
}
@property(nonatomic, copy)NSString* name;
//@property(nonatomic, strong)Bone* bone;
@property(nonatomic, assign)int weight;
@end
NS_ASSUME_NONNULL_END
// Dog.m
// KVC
//
//
#import "Dog.h"
@implementation Dog
- (void)setName:(NSString *)name{
toSetName = name;
}
- (NSString*)getName{
return toSetName;
}
+ (BOOL)accessInstanceVariablesDirectly{
return NO;
}
- (id)valueForUndefinedKey:(NSString *)key{
NSLog(@"error,key%@不存在",key);
return 0;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"error,key%@不存在",key);
}
@end
// main.m
// KVC
//
//
#import <Foundation/Foundation.h>
#import "Dog.h"
#import "Bone.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Dog* dog = [[Dog alloc] init];
[dog setValue:@"huacheng" forKey:@"name"];
NSLog(@"%@",[dog valueForKey:@"toSetName"]);
// Dog* dog = [[Dog alloc]init];
// dog.bone = [[Bone alloc] init];
// [dog setValue:@"huacheng" forKey:@"name"];
// [dog.bone setValue:@"lanzhan" forKey:@"type"];
// NSLog(@"%@",[dog valueForKey:@"name"]);
// NSLog(@"%@",[dog.bone valueForKey:@"type"]);
}
return 0;
}
说明我们在重写方法+ (BOOL)accessInstanceVariablesDirectly;后,没有找到setName方法,不在去找name成员变量,而是直接调用- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;方法。修改函数返回值为YES后,可以得到我们想要的东西
- (id)valueForKey:(NSString *)key;
调用valueForKey:
- 按照get< Key>,,is< key>的顺序方法查找getter方法。如果是BOOL或Int类型,会将其包装成NSNmuber对象。
- 如果getter方法没有找到,KVC则会查找countOf< Key>,objectIn< Key>AtIndex或< Key>AtIndexes等方法。如果countOf< Key>方法和另外两个方法中的一个方法被找到,返回一个可以相应NSArray所有方法的集合代理(NSKeyValueArray,是NSArray的子类)。调用这个集合的代理方法,或者说给这几个代理集合发送属于NSArray的方法。就会以countOf< Key>,objectIn< Key>AtIndex或< Key>AtIndexes这几个方法的组合形式调用。还有一个可选的getKey:range:方法。
- 如果上面的方法没有找到,那么会同时查找countOf< Key>,enumeratorOf< Key>,memberOf< Key>格式的方法。如果这三个方法都找到,返回一个可以响应NSSet的方法的代理集合。给这个集合发送NSSet的消息,就会以countOf< Key>,enumeratorOf< Key>,memberOf< Key>组合的形式调用。
- 如果还没有找到,再检查类方法+ (BOOL)accessInstanceVariablesDirectly;如果返回YES,按照_< key>,is< Key>,key,isKey的顺序搜索成员变量名。如果方法+ (BOOL)accessInstanceVariablesDirectly;返回NO,调用方法- (nullable id)valueForUndefinedKey:(NSString *)key;
- 如果还没有找到,调用方法- (nullable id)valueForUndefinedKey:(NSString *)key
// Array.h
// KVC
//
// Created by 王璐 on 2022/9/21.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Array : NSObject
@property (nonatomic,readwrite,assign) NSUInteger count;
@property (nonatomic,copy) NSString *arrName;
- (void)incrementCount;
- (NSUInteger)countOfNumbers;
- (id)objectInNumbersAtIndex:(NSUInteger)index;
@end
NS_ASSUME_NONNULL_END
// Array.m
// KVC
//
//
#import "Array.h"
@implementation Array
- (void)incrementCount {
self.count++;
}
- (NSUInteger)countOfNumbers {
return self.count;
}
- (id)objectInNumbersAtIndex:(NSUInteger)index { //当key使用numbers时,KVC会找到这两个方法。
return @(index * 2);
}
- (NSInteger)getNum {
return 10;
}
- (NSInteger)num {
return 11;
}
- (NSInteger)isNum {
return 12;
}
@end
// main.m
// KVC
//
//
#import <Foundation/Foundation.h>
#import "Dog.h"
#import "Bone.h"
#import "Array.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Array* arr = [Array new];
NSNumber* num = [arr valueForKey:@"num"];
NSLog(@"%@",num);
id ar = [arr valueForKey:@"numbers"];
NSLog(@"%@",NSStringFromClass([ar class]));
NSLog(@"0:%@ 1:%@ 2:%@ 3:%@",ar[0], ar[1], ar[2], ar[3]);
[arr incrementCount];
NSLog(@"%lu",(unsigned long)[ar count]);
[arr incrementCount];
NSLog(@"%lu",(unsigned long)[ar count]);
[arr setValue:@"newName" forKey:@"arrName"];
NSLog(@"%@",[arr valueForKey:@"arrName"]);
}
return 0;
}
结果:
其他方法举例
NSKeyValueCoding中的其他方法
+ (BOOL)accessInstanceVariablesDirectly;
//默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//KVC提供属性值正确性验证的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回。
- (nullable id)valueForUndefinedKey:(NSString *)key;
//如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//和上一个方法一样,但这个方法是设值。
- (void)setNilValueForKey:(NSString *)key;
//如果你在SetValue方法时面给Value传nil,则会调用这个方法
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
//输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。