调用 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil
系统为我们动态添加了一个NSKVONotifying_ 类名的类,因为我们改变对象属性的值是通过setter方法实现了,所以很明显是系统动态生成的NSKVONotifying_ZJPerson类重写了setter方法。
发现方法实现变了,内部调用了系统Foundation框架下的_NSSetObjectValueAndNotify方法。
_NSSetObjectValueAndNotify函数内部实现过程如下
1. `-[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:]:
2. -[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:usingBlock:]:
3. [ZJPerson setName:];
4. `NSKeyValueDidChange:
5. `NSKeyValueNotifyObserver:
6. - (void)observeValueForKeyPath:ofObject:change:context
简化成OC的伪代码大致如下:
- (void)setName:(NSString *)name{
_NSSetObjectValueAndNotify();
}
void _NSSetObjectValueAndNotify {
[self willChangeValueForKey:@"name"];
[super setName:name];
[self didChangeValueForKey:@"name"];
}
- (void)didChangeValueForKey:(NSString *)key{
[observe observeValueForKeyPath:key ofObject:self change:nil context:nil];
}
可以利用runtime方法打印一下方法列表:
unsigned int count;
Method *methods = class_copyMethodList(object_getClass(self.person), &count);
for (NSInteger index = 0; index < count; index ) {
Method method = methods[index];
NSString *methodStr = NSStringFromSelector(method_getName(method));
NSLog(@"%@n", methodStr);
}
2018-05-20 08:57:07.883400 0800 KVO[35888:3218908] setName:
2018-05-20 08:57:07.883571 0800 KVO[35888:3218908] class
2018-05-20 08:57:07.883676 0800 KVO[35888:3218908] dealloc
2018-05-20 08:57:07.883793 0800 KVO[35888:3218908] _isKVOA
简单分析下重写这些方法的作用:
class:重写这个方法,是为了伪装苹果自动为我们生成的中间类。
dealloc:应该是处理对象销毁之前的一些收尾工作
_isKVOA:告诉系统使用了kvo
自己动手写一个KVO
KVO底层实现还是很复杂的,下面我只是简单的写下实现过程:
- 因为它是一个非正式协议,给NSObject新建一个Category,NSObject kvo.h,添加监听方法:
.h
#import <Foundation/Foundation.h>
@interface NSObject (kvo)
- (void)zj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
@end
.m
#import "NSObject kvo.h"
#import <objc/runtime.h>
#import <objc/message.h>
@implementation NSObject (kvo)
- (void)zj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
//动态添加一个类
NSString *originClassName = NSStringFromClass([self class]);
NSString *newClassName = [@"ZJKVO_" stringByAppendingString:originClassName];
const char *newName = [newClassName UTF8String];
// 继承自当前类,创建一个子类
Class kvoClass = objc_allocateClassPair([self class], newName, 0);
// 添加setter方法
class_addMethod(kvoClass, @selector(setName:), (IMP)setName, "v@:@");
//注册新添加的这个类
objc_registerClassPair(kvoClass);
// 修改isa指针,由ZJPerson指向ZJKVO_Person
object_setClass(self, kvoClass);
// 保存观察者属性到当前类中
objc_setAssociatedObject(self, (__bridge const void *)@"observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark - 重写父类方法
void setName(id self, SEL _cmd, NSString *name) {
// 保存当前KVO的类
Class kvoClass = [self class];
// 将self的isa指针指向父类ZJPerson,调用父类setter方法
object_setClass(self, class_getSuperclass([self class]));
// 调用父类setter方法,重新复制
objc_msgSend(self, @selector(setName:), name);
// 取出ZJKVO_Person观察者
id objc = objc_getAssociatedObject(self, (__bridge const void *)@"observer");
// 通知观察者,执行通知方法
objc_msgSend(objc, @selector(observeValueForKeyPath:ofObject:change:context:), name, self, nil, name);
// 重新修改为ZJKVO_Person类
object_setClass(self, kvoClass);
}