问题1:什么是KVO
答案:
-
KVO
是key-value observing的缩写 -
KVO
是OC对观察者模式又一实现 - 苹果用isa混写(
isa-swizzling
)方式来实现KVO
swizzling: 旋转
问题2: isa混写在KVO中是怎么实现的?
分析:
当我们注册KVO
时候, 会调用这个方法
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
这个方法底层实现为
isa混写原理
-
当我们创建类
Test
调用addObserver :(NSObject *)observer forKeyPath:
这个方法的时候, 系统会为我们动态创建一个NSKVONotifying_Test
的类, 并将test
中的isa
指针指向NSKVONotifying_Test
-
NSKVONotifying_Test
是Test
子类, 目的是重写Test中的setter方法, 来达到实现改变观察者值的目的
上面其实就是KVO的机制和原理
KVO生效
- 使用setter方法改变值KVO才会生效
- 使用setValue:forKey:改变值KVO才会生效
- 成员变量直接修改需手动添加KVO才会生效
KVO代码实现:
- 创建2个类
SRObject
,SRObserver
其中SRObject.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SRObject : NSObject
// 设置变量a, 观察变量a的变化
@property (nonatomic, assign) NSInteger a;
@end
SRObject.m
#import "SRObject.h"
@implementation SRObject
// 初始化方法
- (instancetype)init {
self = [super init];
if (self) {
_a = 0;
}
return self;
}
@end
AppDelegate
中
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// 创建类
SRObject *obj = [[SRObject alloc] init];
// 创建观察者
SRObserver *observer = [[SRObserver alloc] init];
// 调用kvo监听方法, 对a进行监听
// forKeyPath 要与观察变量名字一致
[obj addObserver:observer forKeyPath:@"a" options: NSKeyValueObservingOptionNew context:nil];
// 对a赋值
obj.a = 666;
return YES;
}
SRObserver.m
#import "SRObserver.h"
#import "SRObject.h"
@implementation SRObserver
// KVO回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
// 判断是否是观察的类, 观察的值
if ([object isKindOfClass:[SRObject class]] && [keyPath isEqualToString:@"a"]) {
// 获取值打印结果
NSNumber *testNum = [change valueForKey:NSKeyValueChangeNewKey];
NSLog(@"SRObserver打印结果: %@", testNum);
}
}
@end
创建子类
我们可以先打2个断点, 然后
po
读一下当前类名可以看到当被观察者监听时会创建一个
NSKVONotifying
的子类
运行结果:
运行结果可看到有 SRObserver打印结果: 666
KVO重写Set方法代码:
关键代码
- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;
原理
// NSKVONotifying_A的setter实现
- (void)setValue:(id)obj {
[self willChangeValueForKey:@"a"];
// 子类NSKVONotifying_A调用父类实现, 即原类实现
[super setValue:obj]
[self didChangeValueForKey:@"a"];
}
问题3: 成员变量赋值KVO是否生效
上面例子中的SRObject
中.h, .m 和AppDelegate
我们稍微变化一下
SRObject
内部添加方法改变成员变量
- (void)increase {
_a += 857;
}
AppDelegate
调用一下
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
SRObject *obj = [[SRObject alloc] init];
SRObserver *observer = [[SRObserver alloc] init];
// 调用kvo监听方法
[obj addObserver:observer forKeyPath:@"a" options: NSKeyValueObservingOptionNew context:nil];
//obj.a = 666;
[obj increase];
return YES;
}
运行一下, 可以发现并无监听到, 不能监听到
但是我们可以通过手动KVO方式, 触发生效, 修改increase
方法
- (void)increase {
// 模拟系统写set 方法
[self willChangeValueForKey:@"a"];
_a += 857;
[self didChangeValueForKey:@"a"];
}
手动KVO方式
didChangeValueForKey
方法之后会触发KVO回调
问题4: KVC设置成员变量赋值, KVO是否能生效
上面例子中的AppDelegate
我们稍微变化一下
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
SRObject *obj = [[SRObject alloc] init];
SRObserver *observer = [[SRObserver alloc] init];
// 调用kvo监听方法
[obj addObserver:observer forKeyPath:@"a" options: NSKeyValueObservingOptionNew context:nil];
// obj.a = 666;
// KVC 方法
[obj setValue:@"12345" forKey:@"a"];
return YES;
}
运行结果
KVC中setValue: forKey:
其实调用对象的set方法 与 .a = 666 是一致的
我们可以在SRObject中验证一下KVC是否走了对象set方法 如下
KVC调用set方法验证重写set方法, 可看到KVC也是调用对象set方法
网友评论