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:
的原理
setValue:forKey:
来验证过程
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject {
@public
int _age;
int _isAge;
int age;
int isAge;
}
@end
#import "MJPerson.h"
@implementation MJPerson
- (void)setAge:(int)age
{
NSLog(@"setAge");
}
- (void)_setAge:(int)age
{
NSLog(@"_setAge");
}
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
[person setValue:@10 forKey:@"age"];
}
return 0;
}
断点打印
打印打印看出,成员变量都没赋值,只走了setAge:
方法
注释掉setAge:
方法,再断点打印
#import "MJPerson.h"
@implementation MJPerson
//- (void)setAge:(int)age
//{
// NSLog(@"setAge");
//}
- (void)_setAge:(int)age
{
NSLog(@"_setAge");
}
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
@end
注释掉`setAge:`方法
打印看出,成员变量都没赋值,只走了_setAge:
方法
注释掉_setAge:
方法,accessInstanceVariablesDirectly
返回NO
,再断点打印
@implementation MJPerson
//- (void)setAge:(int)age
//{
// NSLog(@"setAge");
//}
//- (void)_setAge:(int)age
//{
// NSLog(@"_setAge");
//}
+ (BOOL)accessInstanceVariablesDirectly
{
return NO;
}
@end
注释掉`_setAge:`方法,`accessInstanceVariablesDirectly`返回`NO`
打印看出,直接报错NSUnknownKeyException
。
设置accessInstanceVariablesDirectly
返回YES
,再断点打印
#import "MJPerson.h"
@implementation MJPerson
//- (void)setAge:(int)age
//{
// NSLog(@"setAge");
//}
//- (void)_setAge:(int)age
//{
// NSLog(@"_setAge");
//}
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
@end
设置`accessInstanceVariablesDirectly`返回`YES`
打印看出只有_age
被赋值
注释掉_age
,断点打印
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject {
@public
// int _age;
int _isAge;
int age;
int isAge;
}
@end
注释掉`_age`
打印看出只有_isAge
被赋值
注释掉_isAge
,断点打印
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject {
@public
// int _age;
// int _isAge;
int age;
int isAge;
}
@end
注释掉`_isAge`
打印看出只有age
被赋值
注释掉age
,断点打印
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject {
@public
// int _age;
// int _isAge;
// int age;
int isAge;
}
@end
注释掉`age`
打印看出只有isAge
被赋值
注释掉isAge
,断点打印
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject {
@public
// int _age;
// int _isAge;
// int age;
// int isAge;
}
@end
注释掉`isAge`
打印看出,直接报错NSUnknownKeyException
。
看取值valueForKey:
的原理
valueForKey:
来验证过程
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject {
@public
int _age;
int _isAge;
int age;
int isAge;
}
@end
#import "MJPerson.h"
@implementation MJPerson
- (int)getAge
{
return 1;
}
- (int)age
{
return 2;
}
- (int)isAge
{
return 3;
}
- (int)_age
{
return 4;
}
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
person->_age = 6;
person->_isAge = 7;
person->age = 8;
person->isAge = 9;
NSLog(@"age------%@", [person valueForKey:@"age"]);
}
return 0;
}
打印结果
打印结果打印看出,只走了getAge
方法
注释掉getAge
方法
#import "MJPerson.h"
@implementation MJPerson
//- (int)getAge
//{
// return 1;
//}
- (int)age
{
return 2;
}
- (int)isAge
{
return 3;
}
- (int)_age
{
return 4;
}
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
@end
注释掉`getAge`方法
打印看出,只走了age
方法
注释掉age
方法
#import "MJPerson.h"
@implementation MJPerson
//- (int)getAge
//{
// return 1;
//}
//- (int)age
//{
// return 2;
//}
- (int)isAge
{
return 3;
}
- (int)_age
{
return 4;
}
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
@end
注释掉`age`方法
打印看出,只走了isAge
方法
注释掉isAge
方法
#import "MJPerson.h"
@implementation MJPerson
//- (int)getAge
//{
// return 1;
//}
//- (int)age
//{
// return 2;
//}
//- (int)isAge
//{
// return 3;
//}
- (int)_age
{
return 4;
}
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
@end
注释掉`isAge`方法
打印看出,只走了_age
方法
注释掉_age
方法,accessInstanceVariablesDirectly
返回NO
#import "MJPerson.h"
@implementation MJPerson
//- (int)getAge
//{
// return 1;
//}
//- (int)age
//{
// return 2;
//}
//- (int)isAge
//{
// return 3;
//}
//- (int)_age
//{
// return 4;
//}
+ (BOOL)accessInstanceVariablesDirectly
{
return NO;
}
@end
注释掉`_age`方法
打印看出,直接报错NSUnknownKeyException
。
设置accessInstanceVariablesDirectly
返回YES
#import "MJPerson.h"
@implementation MJPerson
//- (int)getAge
//{
// return 1;
//}
//- (int)age
//{
// return 2;
//}
//- (int)isAge
//{
// return 3;
//}
//- (int)_age
//{
// return 4;
//}
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
@end
设置`accessInstanceVariablesDirectly`返回`YES`
打印的是成员变量_age
的值
注释掉成员变量_age
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject {
@public
// int _age;
int _isAge;
int age;
int isAge;
}
@end
注释掉成员变量`_age`
打印的是成员变量_isAge
的值
注释掉成员变量_isAge
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject {
@public
// int _age;
// int _isAge;
int age;
int isAge;
}
@end
注释掉成员变量`_isAge`
打印的是成员变量age
的值
注释掉成员变量age
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject {
@public
// int _age;
// int _isAge;
// int age;
int isAge;
}
@end
注释掉成员变量`age`
打印的是成员变量isAge
的值
注释掉成员变量isAge
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject {
@public
// int _age;
// int _isAge;
// int age;
// int isAge;
}
@end
注释掉成员变量`isAge`
打印看出,直接报错NSUnknownKeyException
。
看一下KVC会不会触发KVO
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject {
int _age;
}
@end
#import "MJPerson.h"
@implementation MJPerson
@end
#import "ViewController.h"
#import "MJPerson.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
MJPerson *person = [[MJPerson alloc] init];
[person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:NULL];
[person setValue:@10 forKey:@"age"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
}
@end
打印结果
打印结果看到,触发了KVO
。但是MJPerson
只有一个成员变量_age
,也就是不会触发setAge:
方法,但是依然触发了KVO
。
通过《KVO的本质分析》,我们知道 在KVC
的时候肯定是执行了willChangeValueForKey:
和didChangeValueForKey:
方法。
面试题
通过KVC修改属性会触发KVO么?
会触发KVO
KVC的赋值和取值过程是怎样的?原理是什么?
把上面两个图讲清楚
网友评论