iOS 底层原理

iOS KVC

2019-09-28  本文已影响0人  天空像天空一样蓝

KVC

KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性

1、基础使用

1.1、常用API

- (void)setValue:(id)value forKey:(NSString *)key;
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;

1.2 使用

创建一个Person类和Cat

@interface Cat : NSObject
@property (assign, nonatomic) int weight;
@end

@interface Person : NSObject
@property (assign, nonatomic) int age;

@property (strong, nonatomic) Cat *cat;
@end
Person *person = [[Person alloc] init];

[person setValue:@10 forKey:@"age"];
NSLog(@"person的age == %d", person.age);
打印结果:
person的age == 10

上面直接给age属性赋值很简单,但是怎么给Cat类里面的weight属性赋值呢?
直接使用调用[person setValue:@99 forKey:@"cat.weight"]这个方法会报经典的系统错误*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x10051ed50> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key cat.weight.找不到这个key

// Cat 必须初始化一下
person.cat = [[Cat alloc] init];
[person setValue:@99 forKeyPath:@"cat.weight"];
打印结果:
cat的weight == 99
[person setValue:@10 forKey:@"age"];
NSLog(@"person的age == %@", [person valueForKey:@"age"]);
打印结果:
person的age == 10
person.cat = [[Cat alloc] init];
[person setValue:@99 forKeyPath:@"cat.weight"];
NSLog(@"cat的weight == %@", [person valueForKeyPath:@"cat.weight"]);
打印结果:
cat的weight == 99

2、底层原理

2.1 setValue: forKey:的原理

原理

我们使用 setValue:forKey:的方法给Person类的age属性赋值。

  1. 先去Person类里面找有没有setAge:有的话传递参数,调用改该方法,否则进行第2步
  2. 找到_setAge:方法,有则传递参数,无进行第3步
  3. 找到accessInstanceVariablesDirectly 这个方法,返回一个Bool值
  1. 会按照_key、_isKey、key、isKey的顺序进行赋值,有则直接赋值,无则进行第5步
  2. 依然会抛出调用setValue:forUndefinedKey: 并抛出异常NSUnknownKeyException这个信息

2.2 valueForKey:的原理

原理
  1. 按照getKey、key、iskey、_key的先后顺序查找方法,如果找到方法直接取值,否则进入第2步
  2. 找到accessInstanceVariablesDirectly 这个方法,返回一个Bool值
  1. 依然会抛出调用valueForUndefinedKey: 并抛出异常NSUnknownKeyException

面试

#import <Foundation/Foundation.h>
@interface MyObserver : NSObject
@end

#import "MyObserver.h"

@implementation MyObserver

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    NSLog(@"observeValueForKeyPath - %@", change);
}

然后调用下面的方法,会发现,打印了上面代码里面的内容

MyObserver *observer = [[MyObserver alloc] init];
MyObserver *person = [[MyObserver 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"];
上一篇 下一篇

猜你喜欢

热点阅读