5OC使用原理 -5- KVC底层原理详解
2020-01-20 本文已影响0人
zysmoon
面试题
1. 通过KVC修改属性会触发KVO么?
- 会触发KVO
代码例子佐证
@interface Person : NSObject
@property (assign, nonatomic) int age;
@end
@interface Observer : NSObject
@end
@implementation Observer
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"observeValueForKeyPath - %@", change);
}
@end
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Observer.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Observer *observer = [[Observer alloc] init];
Person *person = [[Person alloc] init];
// 添加KVO监听
[person addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
// 通过KVC修改age属性
[person setValue:@10 forKey:@"age"];
// 移除KVO监听
[person removeObserver:observer forKeyPath:@"age"];
}
return 0;
}
运行结果
2. KVC的赋值和取值过程是怎样的?原理是什么?
- 详情见下文的valueForKey:的原理图
序言
KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性
-
常见的API有- -(void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- -(void)setValue:(id)value forKey:(NSString *)key;
- -(id)valueForKeyPath:(NSString *)keyPath;
- -(id)valueForKey:(NSString *)key;
setValue:forKey:的原理
1653926-8120f67461e9e1c4.png
代码例子佐证
accessInstanceVariablesDirectly返回NO
// 默认的返回值就是YES
+ (BOOL)accessInstanceVariablesDirectly {
return NO;
}
1653926-787ca18686821c13.png
-
accessInstanceVariablesDirectly返回YES,并且有下划线成员变量
@interface Person : NSObject {
@public
int age;
int isAge;
int _isAge;
int _age;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Person *person = [[Person alloc] init];
// 通过KVC修改age属性
[person setValue:@10 forKey:@"age"];
NSLog(@"%@", [person valueForKey:@"age"]);
}
return 0;
}
运行结果
1653926-3be47169a2d6f037.png
可以将变量依次注释,看看打印结果
age,isAge,_isAge,_age
@public
int age;
int isAge;
// int _isAge;
// int _age;
}
运行结果
1653926-b22e3451c08f3a26.png
valueForKey:的原理
1653926-2bcb47fa08d60154.png
- 代码例子佐证 - 先找方法
@implementation Person
// 第一顺序调用
- (int)getAge {
return 11;
}
// 第二顺序调用
- (int)age {
return 12;
}
// 第三顺序调用
- (int)isAge {
return 13;
}
// 第四顺序调用
- (int)_age {
return 14;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Person *person = [[Person alloc] init];
NSLog(@"%@", [person valueForKey:@"age"]);
}
return 0;
}
- 依次注释掉方法
getAge,age,isAge,_age,然后打印结果如下
1653926-7e56e33c5f848ea7.png
1653926-69b990dc86ccdd97.png
1653926-ebd7d2376f3452e2.png
1653926-6b1200a43a6ac1b6.png
- 代码例子佐证 - 再找成员变量
@interface Person : NSObject {
@public
int age;
int isAge;
int _isAge;
int _age;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Person *person = [[Person alloc] init];
// 通过KVC修改age属性
person->_age = 100;
person->_isAge = 110;
person->age = 120;
person->isAge = 130;
NSLog(@"%@", [person valueForKey:@"age"]);
}
return 0;
}
- 依次在Person.h文件中注释成员变量
_age,_isAge,age,isAge,然后查看打印结果
1653926-95367b05250eae7b.png
1653926-c3ad7fdcf4e3c257.png
1653926-0ba7394647f0607d.png
1653926-3dde6044b5b685e9.png
如果将方法和成员变量全部去掉,打印结果如下
1653926-3a1f26ff20cac2af.png
由打印结果可知,如果方法和成员变量都找不到,则最终方法找不到的错误。
本文参考:
路飞_Luck (https://www.jianshu.com/p/07f7b96bb03f)
以及借鉴MJ的教程视频
非常感谢.