04. KVO使用,原理,本质
2021-01-05 本文已影响0人
ProfessorFan
问题
- KVO日常使用
- KVO原理(KVO本质是什么)
- 如何手动触发KVO
- 直接修改成员变量会触发KVO吗
- KVO图解
- 获取一个Class 对象的方法列表
答案
- KVO日常使用
#import <Foundation/Foundation.h>
@interface MJCat : NSObject
@property (assign, nonatomic) int weight;
@end
@interface MJPerson : NSObject
@property (assign, nonatomic) int age;
@property (assign, nonatomic) MJCat *cat;
@end
@implementation MJCat
@end
@implementation MJPerson
//- (void)setAge:(int)age
//{
// _age = age;
//
// NSLog(@"setAge: - %d", age);
//}
@end
@interface MJObserver : NSObject
@end
@implementation MJObserver
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"observeValueForKeyPath - %@", change);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJObserver *observer = [[MJObserver alloc] init];
MJPerson *person = [[MJPerson alloc] init];
// 添加KVO监听
[person addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
// 通过KVC修改age属性
[person setValue:@10 forKey:@"age"];
// setAge:
// 移除KVO监听
[person removeObserver:observer forKeyPath:@"age"];
// person.age = 10;
// NSLog(@"%@", [person valueForKey:@"age"]);
// NSLog(@"%@", [person valueForKeyPath:@"cat.weight"]);
// NSLog(@"%d", person.age);
// [person setValue:[NSNumber numberWithInt:10] forKey:@"age"];
// [person setValue:@10 forKey:@"age"];
// person.cat = [[MJCat alloc] init];
// [person setValue:@10 forKeyPath:@"cat.weight"];
// NSLog(@"%d", person.age);
}
return 0;
}
- KVO原理(KVO本质是什么)
- 利用RuntimeAPI动态生成一个子类,并且让instance(实例对象)对象的isa指向全新的子类
- 在全新的子类中重写了父类的set方法,当我们修改instance对象的属性的时候,会调用Foundation 的_NSSetXXXValueAndNotify函数
- 源码(set方法)实现流程大概是这样的
a: 第一步先调用willChangeValueForKey:方法
b: 第二步调用父类的setter方法
c: 第三步调用didChangeValueForKey:方法,在方法内部会触发监听器(Oberser)的监听方法(observeValueForKeyPath:ofObject:change:context:)
- 如何手动触发KVO
- 根据KVO原理我们很容易手动触发KVO
1: 第一步实例对象手动调用willChangeValueForKey:方法
2: 第二步实例对象手动调用didChangeValueForKey:方法
- 直接修改成员变量会触发KVO吗
- 直接修改成员变量不会触发KVO,因为直接修改成员变量不会走新子类重写的setter方法,不会去触发willChangeValueForKey: 和 didChangeValueForKey:这两个方法
-
KVO图解
KVO图解.png -
获取一个Class 对象的方法列表
#import <objc/runtime.h>
- (void)printMethodNamesOfClass:(Class)cls
{
unsigned int count;
// 获得方法数组
Method *methodList = class_copyMethodList(cls, &count);
// 存储方法名
NSMutableString *methodNames = [NSMutableString string];
// 遍历所有的方法
for (int i = 0; i < count; i++) {
// 获得方法
Method method = methodList[i];
// 获得方法名
NSString *methodName = NSStringFromSelector(method_getName(method));
// 拼接方法名
[methodNames appendString:methodName];
[methodNames appendString:@", "];
}
// 释放
free(methodList);
// 打印方法名
NSLog(@"%@ %@", cls, methodNames);
}
# 测试
self.person1 = [[MJPerson alloc] init];
self.person1.age = 1;
self.person2 = [[MJPerson alloc] init];
self.person2.age = 2;
// 给person1对象添加KVO监听
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
[self printMethodNamesOfClass:object_getClass(self.person1)];
[self printMethodNamesOfClass:object_getClass(self.person2)];
# 结果
2021-01-05 19:32:33.868603+0800 Interview01[4816:567497] NSKVONotifying_MJPerson setAge:, class, dealloc, _isKVOA,
2021-01-05 19:32:33.868817+0800 Interview01[4816:567497] MJPerson setAge:, age,