设置值的流程
setValue:forKey: setValue:forKeyPath: (keypath 支持 "A.B.C")
-
setKey:_setKey:, 存在这些方法即调用方法, 否则到2 -
+accessInstanceVariablesDirectly, 返回true则到3, 否则到4 -
根据
_key, _isKey, key, isKey的顺序开始查找成员变量, 如果存在成员变量则正常赋值, 否则到4 -
setValue:forUndefinedKey:,默认实现会抛出异常NSUndefinedKeyException
取值的流程
valueForKey: valueForKeyPath:
-
getKeykeyisKey_getKey_key, 存在这些方法即调用方法并返回, 否则到2 -
+accessInstanceVariablesDirectly, 返回true则到3, 否则到4 -
根据
_key, _isKey, key, isKey的顺序开始查找成员变量, 如果存在成员变量则取出值并返回, 否则到4 -
valueForUndefinedKey:,默认实现会抛出异常
KVC是否会触发KVO
利用KVC会触发KVO, 即使该成员变量没有set方法. 在setValue:forKey:中会调用willChangeValueForKey:,didChangeValueForKey:
其他方法
-
setNilValueForKey:默认实现会抛出异常NSInvalidArgumentException当key对应的成员变量的值是基础类型时, 但是传入的value是nil时, 会调用setNilValueForKey: -
- (BOOL)validateValue:(inout id _Nullable __autoreleasing *)ioValue forKey:(NSString *)inKey error:(out NSError *__autoreleasing _Nullable *)outError可以用来验证value是否可用, 可以在这个方法中修改ioValue 或者 outError
用途
Mantle 字典转模型
DEMO
//
// ViewController.m
// TestKVC
//
// Created by 王昱 on 2021/9/28.
//
#import "ViewController.h"
@interface WYPerson: NSObject
{
@public
int _age;
}
@end
@implementation WYPerson
- (void)willChangeValueForKey:(NSString *)key {
NSLog(@"willChangeValueForKey - begin");
[super willChangeValueForKey:key];
NSLog(@"willChangeValueForKey - end");
}
- (void)didChangeValueForKey:(NSString *)key {
NSLog(@"didChangeValueForKey - begin");
[super didChangeValueForKey:key];
NSLog(@"didChangeValueForKey - end");
}
- (void)setNilValueForKey:(NSString *)key {
NSLog(@"setNilValueForKey - %@", key);
}
- (BOOL)validateValue:(inout id _Nullable __autoreleasing *)ioValue forKey:(NSString *)inKey error:(out NSError *__autoreleasing _Nullable *)outError {
if([inKey isEqualToString:@"age"]){
if([(*ioValue) isKindOfClass:[NSNumber class]]) {
if([(NSNumber *)(*ioValue) intValue] < 0) {
*ioValue = @20;
}
return YES;
} else {
*outError = [NSError errorWithDomain:@"type exception" code:0 userInfo:nil];
return NO;
}
}
return YES;
}
@end
@interface ViewController ()
@property (nonatomic, strong) WYPerson *person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.person = [WYPerson new];
[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
/*
2021-09-28 16:58:06.203026+0800 TestKVC[41063:6884574] willChangeValueForKey - begin
2021-09-28 16:58:06.203196+0800 TestKVC[41063:6884574] willChangeValueForKey - end
2021-09-28 16:58:06.203432+0800 TestKVC[41063:6884574] didChangeValueForKey - begin
2021-09-28 16:58:06.203810+0800 TestKVC[41063:6884574] keypath: age
object: <WYPerson: 0x600000590360>
change: {
kind = 1;
new = 10;
old = 0;
}
context: (null)
2021-09-28 16:58:06.203898+0800 TestKVC[41063:6884574] didChangeValueForKey - end
*/
[self.person setValue:@10 forKey:@"age"];
NSLog(@"setValue:forKey: - end");
/**
2021-09-28 17:08:29.784955+0800 TestKVC[41659:6898908] willChangeValueForKey - begin
2021-09-28 17:08:29.785021+0800 TestKVC[41659:6898908] willChangeValueForKey - end
2021-09-28 17:08:29.785095+0800 TestKVC[41659:6898908] setNilValueForKey - age
2021-09-28 17:08:29.785162+0800 TestKVC[41659:6898908] didChangeValueForKey - begin
2021-09-28 17:08:29.785281+0800 TestKVC[41659:6898908] keypath: age
object: <WYPerson: 0x600000330610>
change: {
kind = 1;
new = 10;
old = 10;
}
context: (null)
2021-09-28 17:08:29.785354+0800 TestKVC[41659:6898908] didChangeValueForKey - end
*/
[self.person setValue:nil forKey:@"age"];
// 2021-09-28 17:19:55.815113+0800 TestKVC[42335:6915549] Error Domain=type exception Code=0 "(null)"
// id number = @"";
/*
2021-09-28 17:21:29.732385+0800 TestKVC[42445:6918021] willChangeValueForKey - begin
2021-09-28 17:21:29.732459+0800 TestKVC[42445:6918021] willChangeValueForKey - end
2021-09-28 17:21:29.739948+0800 TestKVC[42445:6918021] didChangeValueForKey - begin
2021-09-28 17:21:29.740109+0800 TestKVC[42445:6918021] keypath: age
object: <WYPerson: 0x600001744160>
change: {
kind = 1;
new = 20;
old = 10;
}
context: (null)
2021-09-28 17:21:29.740192+0800 TestKVC[42445:6918021] didChangeValueForKey - end
*/
// id number = @(-5);
/**
2021-09-28 17:22:00.405500+0800 TestKVC[42484:6919089] willChangeValueForKey - begin
2021-09-28 17:22:00.405570+0800 TestKVC[42484:6919089] willChangeValueForKey - end
2021-09-28 17:22:00.413856+0800 TestKVC[42484:6919089] didChangeValueForKey - begin
2021-09-28 17:22:00.414028+0800 TestKVC[42484:6919089] keypath: age
object: <WYPerson: 0x600002b801a0>
change: {
kind = 1;
new = 10;
old = 10;
}
context: (null)
2021-09-28 17:22:00.414109+0800 TestKVC[42484:6919089] didChangeValueForKey - end
*/
id number = @10;
NSError *error = nil;
if([self.person validateValue:&number forKey:@"age" error:&error]) {
[self.person setValue:number forKey:@"age"];
} else {
NSLog(@"%@", error);
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@" keypath: %@ \n object: %@ \n change: %@ \n context: %@", keyPath, object, change, context);
}
- (void)dealloc {
[self.person removeObserver:self forKeyPath:@"age"];
}
@end