参考官方文档(developer.apple.com/library/arc… )
KVO(Key-Value Observing)键值观察,是观察者模式的一种衍生。基本思想就是:通过KVO接口方法,给对象属性添加观察,并指定监听观察者,被观察对象的属性发生变化时,可以触发观察者实现的KVO接口方法,来自动通知监听观察者
- KVO也是NSObject的一种非正式协议,凡是继承了NSObject的对象都可以使用KVO. KVO提供了一种机制,该机制允许将其他对象的特定属性的更改通知对象。iOS 应用程序MVC架构模式中模型层和控制器层之间的通信会经常用到KVO。在OS X中,控制器层绑定技术(Cocoa Bingdings)在很大程度上依赖于键值观察.
- 控制器对象通常观察模型对象的属性,而视图对象通过控制器观察模型对象的属性。
- 模型对象可以观察其他模型对象(通常用于确定从属值何时更改),甚至可以观察自身(再次确定从属值何时更改)
来看下官方文档,KVO实现细节:
KVO实现细节
接下来我们用代码深入探讨下KVO到底做了些什么?上代码
#import "ViewController.h"
@interface Person : NSObject
@end
@implementation Person
//对类方法(类对象)进行动态方法决议(亦称方法动态解析)
+ (BOOL)resolveClassMethod:(SEL)sel{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(sel));
return [super resolveClassMethod:sel];
}
//对实例方法(实例对象)进行动态方法决议(亦称方法动态解析)
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(sel));
return [super resolveClassMethod:sel];
}
//动态方法决议失败后,首先查找备用接收者,是否能直接处理消息(亦称快速转发)
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(aSelector));
return [super forwardingTargetForSelector:aSelector];
}
//动态方法决议失败后,查找备用接收者也失败(快速转发失败),那么就进行慢转发(需要进行方法签名)
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s - %@",__func__ ,anInvocation);
return [super forwardInvocation:anInvocation];
}
//对慢转发进行方法签名处理
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(aSelector));
return [super methodSignatureForSelector:aSelector];
}
- (IMP)methodForSelector:(SEL)aSelector{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(aSelector));
return [super methodForSelector:aSelector];
}
- (void)doesNotRecognizeSelector:(SEL)aSelector{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(aSelector));
return [super doesNotRecognizeSelector:aSelector];
}
//是否允许自动触发KVO
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
NSLog(@"%s",__func__);
return [super automaticallyNotifiesObserversForKey:key];
}
//是否允许直接获取实例
+ (BOOL)accessInstanceVariablesDirectly{
NSLog(@"%s",__func__);
return [super accessInstanceVariablesDirectly];
}
@end
@interface ViewController ()
@property (nonatomic, strong) Person * person;
@property (nonatomic ,assign) IMP imp,imp2,imp3,classImp,classImp2,classImp3;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_person = [[Person alloc] init];
//添加属性观察,注意这里Person类并没有添加任何属性
[_person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%s",__func__);
NSLog(@"keyPath = %@",keyPath);
NSLog(@"change = %@",change);
}
- (void)dealloc
{
NSLog(@"%s",__func__);
//移除
[_person removeObserver:self forKeyPath:@"age"];
}
@end
运行结果如下:
2019-12-27 09:44:14.613488+0800 KVC&KVO[1353:39658] +[Person resolveClassMethod:] - keyPathsForValuesAffectingAge
2019-12-27 09:44:14.613624+0800 KVC&KVO[1353:39658] +[Person automaticallyNotifiesObserversForKey:]
2019-12-27 09:44:14.613719+0800 KVC&KVO[1353:39658] +[Person automaticallyNotifiesObserversOfAge]
2019-12-27 09:44:14.613844+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - setAge:
2019-12-27 09:44:14.613946+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - _setAge:
2019-12-27 09:44:14.614041+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - setIsAge:
2019-12-27 09:44:14.614126+0800 KVC&KVO[1353:39658] +[Person accessInstanceVariablesDirectly]
2019-12-27 09:44:14.614227+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - getAge
2019-12-27 09:44:14.614460+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - age
2019-12-27 09:44:14.614869+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - isAge
2019-12-27 09:44:14.615145+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - _getAge
2019-12-27 09:44:14.615406+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - _age
2019-12-27 09:44:14.615723+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - countOfAge
2019-12-27 09:44:14.615869+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - objectInAgeAtIndex:
2019-12-27 09:44:14.616045+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - ageAtIndexes:
2019-12-27 09:44:14.616200+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - indexInAgeOfObject:
2019-12-27 09:44:14.616381+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - enumeratorOfAge
2019-12-27 09:44:14.616540+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - memberOfAge:
2019-12-27 09:44:14.619180+0800 KVC&KVO[1353:39658] +[Person accessInstanceVariablesDirectly]
2019-12-27 09:44:14.619283+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - insertObject:inAgeAtIndex:
2019-12-27 09:44:14.619391+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - insertAge:atIndexes:
2019-12-27 09:44:14.619484+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - removeObjectFromAgeAtIndex:
2019-12-27 09:44:14.619584+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - removeAgeAtIndexes:
2019-12-27 09:44:14.619698+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - insertObject:inAgeAtIndex:
2019-12-27 09:44:14.619791+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - insertAge:atIndexes:
2019-12-27 09:44:14.619884+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - removeObjectFromAgeAtIndex:
2019-12-27 09:44:14.619974+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - removeAgeAtIndexes:
2019-12-27 09:44:14.620089+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - addAgeObject:
2019-12-27 09:44:14.620274+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - removeAge:
2019-12-27 09:44:14.620441+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - removeAgeObject:
2019-12-27 09:44:14.620610+0800 KVC&KVO[1353:39658] +[Person resolveInstanceMethod:] - addAge:
结果分析:
-
1.首先Person类并没有添加任何age属性,我们仍然注册观察Person对象属性age进行监听(这里不建议对一个不存在的属性进行观察,会带来异常),通过log你会看到KVC的影子,或许正好验证了 KVO是基于KVC的说法.
-
2.Person类并没有age属性,添加KVO观察后,使用了动态方法解析,首先keyPathsForValuesAffectingAge(这里的Age就是我们的age属性)这个方法解析通过(因为并没有走消息转发流程,此方法主要来指明age是依赖哪些键,可以跟随依赖键更新而更新age自身)
-
3.动态方法解析keyPathsForValuesAffectingAge成功后,然后调用automaticallyNotifiesObserversForKey:和 automaticallyNotifiesObserversOfAge是否允许Age这个key自动触发KVO(这里默认都是返回YES允许的),如果返回YES,并且option没有指定NSKeyValueObservingOptionInitial(因为这个并不会被拦截),则开始进行KVC对属性age进行处理,否则,直接返回,没有进行KVC的必要了(这里你可以重写automaticallyNotifiesObserversForKey:返回NO 或者 automaticallyNotifiesObserversOfAge返回NO,它确实停止了KVC流程)
-
4.如果允许对age属性自动KVO,那么接下来就对age属性开始执行KVC流程 (注意option指定NSKeyValueObservingOptionInitial,则会抛出NSUnknownKeyException崩溃,因为KVC首先走age属性的getter流程,发现没有getter一类方法,也没有实例变量,会动态解析生成getPrimitiveAge和primitiveAge方法,但同时并没有生成age这个实例变量,所以Peson类没有age这个key导致崩溃)
-
5.KVC首先走age属性的setter流程(注意option指定NSKeyValueObservingOptionInitial则会先走getter流程),发现没有setter一类的存取方法,就去调用accessInstanceVariablesDirectly,查看是否允许直接获取实例变量(这里是允许),如果返回NO不允许,则会去动态解析setPrimitiveAge:,getPrimitiveAge 和 primitiveAge 方法,保证能够正确处理KVO. 注意此时KVC对age进行取值或者设值操作 都会抛出valueForUndefinedKey异常.同理接着顺序处理age属性的getter流程,以及age集合属性处理流程
KVO的使用
主要分三步:
1.添加属性监听
对简单非集合对象:
-(void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
对于NSArray集合对象:
-(void)addObserver:(NSObject *)observer toObjectsAtIndexes:(NSIndexSet *)indexes forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
2.实现观察者监听回调接口:
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;
3.移除监听
重要:添加观察后,不需要监听的时候必须移除监听.一般在dealloc方法里移除
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
或者
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
在NSObject分类NSKeyValueObserverRegistration中有以下常用方法:
/*
添加键值观察
observer:观察者,也就是通知的订阅者(或者说监听者),观察者是必须的,而且不能为空
keyPath :被观察的属性
options :KVO配置相关,既会影响通知中提供的更改字典的内容,又会影响生成通知的方式,有四个选项,下面会介绍
context :上下文,主要区分通知来源
*/
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
/*移除监听
注意:必须保证在观察者被释放销毁之前移除,否则程序崩溃
*/
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
/*
* 移除监听
注意:必须保证在观察者被释放销毁之前移除,否则程序崩溃
*/
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
NSKeyValueObservingOptions有四个选项
NSKeyValueObservingOptionNew 观察者回调监听中change字典中包含改变后的值
NSKeyValueObservingOptionOld 观察者回调监听中change字典中包含改变前的值
NSKeyValueObservingOptionInitial 注册后立刻触发KVO通知,但是需要注意的是 NSKeyValueObservingOptions参数同时指定了NSKeyValueObservingOptionOld | NSKeyValueObservingOptionInitial,首次触发KVO change字典中并不包含old值
NSKeyValueObservingOptionPrior 值改变前是否也通知(改变前通知一次,改变后再通知一次)
简单属性使用KVO
来看点例子:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
{
@public //暴露成员变量,仅仅为了演示给成员变量直接赋值,验证不能触发KVO
NSUInteger _age;
}
-(void)setAge:(NSUInteger)age;
@end
NS_ASSUME_NONNULL_END
#import "Person.h"
@interface Person ()
@end
@implementation Person
-(void)setAge:(NSUInteger)age{
NSLog(@"%s",__func__);
_age = age;
}
@end
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) Person * person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_person = [[Person alloc] init];
//添加属性观察
[_person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
//注意这里直接给成员变量赋值,不会触发KVO
_person->_age = 20;
//这里调用自定义setter方法,验证是否会触发KVO
[_person setAge:65];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%s",__func__);
NSLog(@"keyPath = %@",keyPath);
NSLog(@"change = %@",change);
}
- (void)dealloc
{
NSLog(@"%s",__func__);
//移除
[_person removeObserver:self forKeyPath:@"age"];
}
@end
运行结果分析: 我们可以看到_person->_age = 20 直接赋值并没有触发KVO(你可以将 [_person setAge:65];注释掉去验证,它确实没有触发KVO),而调用setter方法触发了KVO,由此可以猜测setter方法触发了KVO.但是有一点一定很疑惑,因为才刚说了直接赋值操作不能触发KVO,而我们的setter方法仅仅做了一个赋值操作,但结果确很意外,它竟然触发了KVO!由此可以下结论了,被观察的对象类中setter方法是可有可无的,它不是必须的!!!接下来我们将Person类中setter方法注释掉。由于现在没有明确的存取方法了,这个时候KVC可以来展示下自己的才能了!我们将 [_person setAge:65] 换成KVC方法 [_person setValue:@25 forKey:@"age"];并且Persron添加如下方法
+ (BOOL)accessInstanceVariablesDirectly{
NSLog(@"%s",__func__);
return [super accessInstanceVariablesDirectly];
}
运行结果如我们预期猜想的那样,但是也有疑惑,因为看到KVC打印结果,也是直接获取的对象实例,我们也没有提供setter方法。
对象类中setter方法确实不是必须的.那么问题又来了,究竟是什么触发的KVO? 官方文档中手动KVO有这么一段:
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) Person * person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_person = [[Person alloc] init];
//添加属性观察
[_person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
[_person willChangeValueForKey:@"age"];
_person->_age = 30;
[_person didChangeValueForKey:@"age"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%s",__func__);
NSLog(@"keyPath = %@",keyPath);
NSLog(@"change = %@",change);
}
- (void)dealloc
{
NSLog(@"%s",__func__);
//移除
[_person removeObserver:self forKeyPath:@"age"];
}
@end
运行结果也如预期的一样触发KVO( willChangeValueForKey:和 didChangeValueForKey:确实触发KVO通知了,中间直接赋值操作可有可无)
- 1.对象添加注册观察时,采用了派生类机制,父类isa指针指向派生类,并重写父类setter方法,并加入了willChangeValueForKey:和didChangeValueForKey:方法
- 2.对象添加注册观察时,使用了runtime动态方法解析setter(注意并没有添加setter方法),在改变实例变量的前后(也即赋值操作前后)分别注入了willChangeValueForKey:和didChangeValueForKey:方法 触发KVO通知
接下来我们来验证第1种猜想 代码如下:
其中Person.m增加方法resolveInstanceMethod: 便于验证
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(sel));
return [super resolveClassMethod:sel];
}
#import "ViewController.h"
#import "Person.h"
#import <objc/message.h>
@interface ViewController ()
@property (nonatomic, strong) Person * person;
@property (nonatomic ,assign) IMP imp,imp2,imp3,classImp,classImp2,classImp3;
@end
@implementation ViewController
//获取对象所有属性
NSArray<NSString*>*getAllProperties(Class cls){
unsigned int count;
objc_property_t * properties = class_copyPropertyList(cls, &count);
NSMutableArray* arr = [NSMutableArray array];
for (int i = 0; i < count; i++) {
objc_property_t property= properties[i];
const char * p_name = property_getName(property);
NSString* name = [NSString stringWithCString:p_name encoding:NSUTF8StringEncoding];
[arr addObject:name];
}
free(properties);
return arr.copy;
}
//获取对象所有实例变量
NSArray<NSString*>*getAllIvars(Class cls){
unsigned int count;
Ivar * ivars = class_copyIvarList(cls, &count);
NSMutableArray* arr = [NSMutableArray array];
for (int i = 0; i < count; i++) {
Ivar ivar= ivars[i];
const char * ivar_name = ivar_getName(ivar);
NSString* name = [NSString stringWithCString:ivar_name encoding:NSUTF8StringEncoding];
[arr addObject:name];
}
free(ivars);
return arr.copy;
}
//获取对象所有方法
NSArray<NSString*>*getAllMethods(Class cls){
unsigned int count;
Method * methods = class_copyMethodList(cls, &count);
NSMutableArray* arr = [NSMutableArray array];
for (int i = 0; i < count; i++) {
Method method = methods[i];
SEL sel = method_getName(method);
NSString* name = NSStringFromSelector(sel);
[arr addObject:name];
}
free(methods);
return arr.copy;
}
- (void)viewDidLoad {
[super viewDidLoad];
_person = [[Person alloc] init];
Class classP = object_getClass(_person);
//观察前
NSLog(@"观察前对象 :%@",_person);
NSLog(@"观察前对象类 :%@",classP);
NSLog(@"观察前对象父类 :%@",[classP superclass]);
NSLog(@"观察前所有属性 :%@",getAllProperties(classP));
NSLog(@"观察前所有变量 :%@",getAllIvars(classP));
NSLog(@"观察前所有方法 :%@",getAllMethods(classP));
self.imp = [_person methodForSelector:@selector(setAge:)];
self.classImp = [_person methodForSelector:@selector(class)];
//添加属性观察
[_person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
//触发KVO
[_person setAge:200];
//观察后
Class classP2 = object_getClass(_person);// 与 _person->isa等价 注意这里不能_person->isa调用,因为默认isa是@protected
NSLog(@"观察后对象 :%@",_person);
NSLog(@"观察后对象类:%@",classP2);
NSLog(@"观察后对象父类 :%@",[classP2 superclass]);
NSLog(@"观察后所有属性 :%@",getAllProperties(classP2));
NSLog(@"观察后所有变量 :%@",getAllIvars(classP2));
NSLog(@"观察后所有方法 :%@",getAllMethods(classP2));
self.imp2 = [_person methodForSelector:@selector(setAge:)];
self.classImp2 = [_person methodForSelector:@selector(class)];
//移除观察,一般放在dealloc方法移除,这里仅仅为了演示移除观察后的效果
[_person removeObserver:self forKeyPath:@"age"];
Class classP3 = object_getClass(_person);
NSLog(@"移除观察后对象 :%@",_person);
NSLog(@"移除观察后对象类:%@",classP3);
NSLog(@"移除观察后对象父类 :%@",[classP3 superclass]);
NSLog(@"移除观察后所有属性 :%@",getAllProperties(classP3));
NSLog(@"移除观察后所有变量 :%@",getAllIvars(classP3));
NSLog(@"移除观察后所有方法 :%@",getAllMethods(classP3));
self.imp3 = [_person methodForSelector:@selector(setAge:)];
self.classImp3 = [_person methodForSelector:@selector(class)];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%s",__func__);
NSLog(@"keyPath = %@",keyPath);
NSLog(@"change = %@",change);
}
//- (void)dealloc
//{
// NSLog(@"%s",__func__);
// //移除
// [_person removeObserver:self forKeyPath:@"age"];
//}
@end
打印有点多,具体看log吧
2019-12-26 14:20:01.558958+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - get_isKVOA
2019-12-26 14:20:01.559114+0800 KVC&KVO[3067:148054] 观察前_isKVOA返回值:0
2019-12-26 14:20:01.559212+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - _dynamicContextEvaluation:patternString:
2019-12-26 14:20:01.559305+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - descriptionWithLocale:
2019-12-26 14:20:01.559403+0800 KVC&KVO[3067:148054] 观察前对象 :<Person: 0x6000008745e0>
2019-12-26 14:20:01.559489+0800 KVC&KVO[3067:148054] +[Person resolveClassMethod:] - _dynamicContextEvaluation:patternString:
2019-12-26 14:20:01.559576+0800 KVC&KVO[3067:148054] +[Person resolveClassMethod:] - descriptionWithLocale:
2019-12-26 14:20:01.559685+0800 KVC&KVO[3067:148054] 观察前对象类 :Person
2019-12-26 14:20:01.559933+0800 KVC&KVO[3067:148054] 观察前对象父类 :NSObject
2019-12-26 14:20:01.560332+0800 KVC&KVO[3067:148054] 观察前所有属性 :(
)
2019-12-26 14:20:01.560978+0800 KVC&KVO[3067:148054] 观察前所有变量 :(
"_age"
)
2019-12-26 14:20:01.561398+0800 KVC&KVO[3067:148054] 观察前所有方法 :(
"setAge:"
)
2019-12-26 14:20:01.561817+0800 KVC&KVO[3067:148054] +[Person resolveClassMethod:] - keyPathsForValuesAffectingAge
2019-12-26 14:20:01.562166+0800 KVC&KVO[3067:148054] +[Person resolveClassMethod:] - automaticallyNotifiesObserversOfAge
2019-12-26 14:20:01.562608+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - getAge
2019-12-26 14:20:01.562920+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - age
2019-12-26 14:20:01.563081+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - isAge
2019-12-26 14:20:01.563286+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - _getAge
2019-12-26 14:20:01.563470+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - _age
2019-12-26 14:20:01.563638+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - countOfAge
2019-12-26 14:20:01.567939+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - objectInAgeAtIndex:
2019-12-26 14:20:01.568043+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - ageAtIndexes:
2019-12-26 14:20:01.568134+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - indexInAgeOfObject:
2019-12-26 14:20:01.568232+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - enumeratorOfAge
2019-12-26 14:20:01.568324+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - memberOfAge:
2019-12-26 14:20:01.568427+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - insertObject:inAgeAtIndex:
2019-12-26 14:20:01.568520+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - insertAge:atIndexes:
2019-12-26 14:20:01.568606+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - removeObjectFromAgeAtIndex:
2019-12-26 14:20:01.568782+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - removeAgeAtIndexes:
2019-12-26 14:20:01.569057+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - insertObject:inAgeAtIndex:
2019-12-26 14:20:01.569271+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - insertAge:atIndexes:
2019-12-26 14:20:01.569484+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - removeObjectFromAgeAtIndex:
2019-12-26 14:20:01.569751+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - removeAgeAtIndexes:
2019-12-26 14:20:01.570040+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - addAgeObject:
2019-12-26 14:20:01.570286+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - removeAge:
2019-12-26 14:20:01.570553+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - removeAgeObject:
2019-12-26 14:20:01.570863+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - addAge:
2019-12-26 14:20:01.571093+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - getAge
2019-12-26 14:20:01.571345+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - age
2019-12-26 14:20:01.571596+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - isAge
2019-12-26 14:20:01.571841+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - _getAge
2019-12-26 14:20:01.572090+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - _age
2019-12-26 14:20:01.572272+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - countOfAge
2019-12-26 14:20:01.572501+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - objectInAgeAtIndex:
2019-12-26 14:20:01.572756+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - ageAtIndexes:
2019-12-26 14:20:01.573008+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - indexInAgeOfObject:
2019-12-26 14:20:01.573191+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - enumeratorOfAge
2019-12-26 14:20:01.573421+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - memberOfAge:
2019-12-26 14:20:01.573698+0800 KVC&KVO[3067:148054] -[Person setAge:]
2019-12-26 14:20:01.573972+0800 KVC&KVO[3067:148054] -[ViewController observeValueForKeyPath:ofObject:change:context:]
2019-12-26 14:20:01.574154+0800 KVC&KVO[3067:148054] keyPath = age
2019-12-26 14:20:01.574458+0800 KVC&KVO[3067:148054] change = {
kind = 1;
new = 300;
old = 0;
}
2019-12-26 14:20:01.574798+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - get_isKVOA
2019-12-26 14:20:01.575064+0800 KVC&KVO[3067:148054] 观察后_isKVOA返回值:1
2019-12-26 14:20:01.575333+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - _dynamicContextEvaluation:patternString:
2019-12-26 14:20:01.575560+0800 KVC&KVO[3067:148054] +[Person resolveInstanceMethod:] - descriptionWithLocale:
2019-12-26 14:20:01.575835+0800 KVC&KVO[3067:148054] 观察后对象 :<Person: 0x6000008745e0>
2019-12-26 14:20:01.576035+0800 KVC&KVO[3067:148054] +[Person resolveClassMethod:] - _dynamicContextEvaluation:patternString:
2019-12-26 14:20:01.576254+0800 KVC&KVO[3067:148054] +[Person resolveClassMethod:] - descriptionWithLocale:
2019-12-26 14:20:01.576486+0800 KVC&KVO[3067:148054] 观察后对象类:NSKVONotifying_Person
2019-12-26 14:20:01.576697+0800 KVC&KVO[3067:148054] +[Person resolveClassMethod:] - _dynamicContextEvaluation:patternString:
2019-12-26 14:20:01.576935+0800 KVC&KVO[3067:148054] +[Person resolveClassMethod:] - descriptionWithLocale:
2019-12-26 14:20:01.577174+0800 KVC&KVO[3067:148054] 观察后对象父类 :Person
2019-12-26 14:20:01.577406+0800 KVC&KVO[3067:148054] 观察后所有属性 :(
)
2019-12-26 14:20:01.577623+0800 KVC&KVO[3067:148054] 观察后所有变量 :(
)
2019-12-26 14:20:01.577849+0800 KVC&KVO[3067:148054] 观察后所有方法 :(
"setAge:",
class,
dealloc,
"_isKVOA"
)
2019-12-26 14:20:01.578021+0800 KVC&KVO[3067:148054] 移除观察后_isKVOA返回值:0
2019-12-26 14:20:05.452737+0800 KVC&KVO[3067:148054] 移除观察后对象 :<Person: 0x6000008745e0>
2019-12-26 14:20:05.452894+0800 KVC&KVO[3067:148054] 移除观察后对象类:Person
2019-12-26 14:20:05.452995+0800 KVC&KVO[3067:148054] 移除观察后对象父类 :NSObject
2019-12-26 14:20:05.453081+0800 KVC&KVO[3067:148054] 移除观察后所有属性 :(
)
2019-12-26 14:20:05.453169+0800 KVC&KVO[3067:148054] 移除观察后所有变量 :(
"_age"
)
2019-12-26 14:20:05.453263+0800 KVC&KVO[3067:148054] 移除观察后所有方法 :(
"setAge:"
)
运行结果分析:
- 1.观察前后以及移除观察后,被观察对象_person地址没有变化
- 2.观察后,动态生成了Person类的一个子类 NSKVONotifying_Person,并且_person实例对象isa指向NSKVONotifying_Person
- 3.子类NSKVONotifying_Person重写setAge:方法,由KVC&KVO(这里KVC&KVO是我项目名称)-[Person setAge:] 变成了Foundation_NSSetUnsignedLongLongValueAndNotify方法,并且重写的setter方法中加入了 //值改变之前 [self willChangeValueForKey:@"age"]; [super setAge:age]; //因为它调用了父类的setAge:方法 //值改变之后 [self didChangeValueForKey:@"age"]; 这样就触发了KVO
- 4.子类NSKVONotifying_Person重写了class方法,由[NSObject class]变成了Foundation`NSKVOClass
- 5.子类NSKVONotifying_Person重写了dealloc方法(NSObject基类中存在dealloc方法),做一些清理工作
- 6.Person类及其子类NSKVONotifying_Person都动方法解析_isKVOA方法(此方法NSObejct基类中并没有,可以看到在观察前和观察后,动态方法解析了_isKVOA方法,并在观察后增加了_isKVOA方法),猜测用来标记对象是否被添加KVO观察了
- 7.移除观察后,_person实例对象isa指向Person类
- 8.观察前和移除观察后 _isKOVA均返回false ,而添加观察后_isKOVA返回true,由此基本可以确定_isKOVA用来标记当前对象是否被添加观察
到这里理论上说完了,但是还存在一种情况,就是父类Person没有提供setAge:方法,只提供一个成员变量_age,但是使用KVC同样可以触发KVO,这又是为什么呢?下面通过结合KVC来验证,这里使用 [_person setValue:@300 forKey:@"age"];来触发KVO
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
{
@public //暴露成员变量,仅仅为了演示给成员变量直接赋值,验证不能触发KVO
NSUInteger _age;
}
@end
NS_ASSUME_NONNULL_END
#import "Person.h"
@interface Person ()
@end
@implementation Person
+ (BOOL)accessInstanceVariablesDirectly{
NSLog(@"%s",__func__);
return [super accessInstanceVariablesDirectly];
}
@end
运行结果分析: 这里Person并没有提供setAge:方法,使用KVC设值,也是直接获取的对象实例,注册KVO后,同样生成了一个派生类NSKVONotifying_Person,这个派生类没有添加setter方法,只重写了dealloc方法和class方法,增加了一个_isKVOA方法,不同的是imp和imp2变成了_objc_msgForward,这样类似于上面的猜想2,使用了runtime动态方法解析或转发,接下来我们就向Person类种重写消息转发的几个常用方法:
#import "Person.h"
@interface Person ()
@end
@implementation Person
+ (BOOL)resolveClassMethod:(SEL)sel{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(sel));
return [super resolveClassMethod:sel];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(sel));
return [super resolveClassMethod:sel];
}
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(aSelector));
return [super forwardingTargetForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s - %@",__func__ ,anInvocation);
return [super forwardInvocation:anInvocation];
}
- (IMP)methodForSelector:(SEL)aSelector{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(aSelector));
return [super methodForSelector:aSelector];
}
- (void)doesNotRecognizeSelector:(SEL)aSelector{
NSLog(@"%s - %@",__func__ ,NSStringFromSelector(aSelector));
return [super doesNotRecognizeSelector:aSelector];
}
+ (BOOL)accessInstanceVariablesDirectly{
NSLog(@"%s",__func__);
return [super accessInstanceVariablesDirectly];
}
@end
运行结果如下(可以顺便看下KVC执行流程):
2019-12-26 10:52:51.962332+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - _dynamicContextEvaluation:patternString:
2019-12-26 10:52:51.962469+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - descriptionWithLocale:
2019-12-26 10:52:51.962572+0800 KVC&KVO[1368:47095] 观察前对象 :<Person: 0x600003894530>
2019-12-26 10:52:51.962659+0800 KVC&KVO[1368:47095] +[Person resolveClassMethod:] - _dynamicContextEvaluation:patternString:
2019-12-26 10:52:51.962744+0800 KVC&KVO[1368:47095] +[Person resolveClassMethod:] - descriptionWithLocale:
2019-12-26 10:52:51.962824+0800 KVC&KVO[1368:47095] 观察前对象类 :Person
2019-12-26 10:52:51.962902+0800 KVC&KVO[1368:47095] 观察前对象父类 :NSObject
2019-12-26 10:52:51.963032+0800 KVC&KVO[1368:47095] 观察前所有属性 :(
)
2019-12-26 10:52:51.963425+0800 KVC&KVO[1368:47095] 观察前所有变量 :(
"_age"
)
2019-12-26 10:52:51.963819+0800 KVC&KVO[1368:47095] 观察前所有方法 :(
"doesNotRecognizeSelector:",
"forwardingTargetForSelector:",
"methodForSelector:",
"forwardInvocation:"
)
2019-12-26 10:52:51.964023+0800 KVC&KVO[1368:47095] -[Person methodForSelector:] - setAge:
2019-12-26 10:52:51.964311+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - setAge:
2019-12-26 10:52:51.964684+0800 KVC&KVO[1368:47095] -[Person methodForSelector:] - class
2019-12-26 10:52:51.965136+0800 KVC&KVO[1368:47095] +[Person resolveClassMethod:] - keyPathsForValuesAffectingAge
2019-12-26 10:52:51.965465+0800 KVC&KVO[1368:47095] +[Person resolveClassMethod:] - automaticallyNotifiesObserversOfAge
2019-12-26 10:52:51.965831+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - setAge:
2019-12-26 10:52:51.966028+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - _setAge:
2019-12-26 10:52:51.966324+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - setIsAge:
2019-12-26 10:52:51.973082+0800 KVC&KVO[1368:47095] +[Person accessInstanceVariablesDirectly]
2019-12-26 10:52:51.973216+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - getAge
2019-12-26 10:52:51.973330+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - age
2019-12-26 10:52:51.973428+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - isAge
2019-12-26 10:52:51.973524+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - _getAge
2019-12-26 10:52:51.973612+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - _age
2019-12-26 10:52:51.973702+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - countOfAge
2019-12-26 10:52:51.973792+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - objectInAgeAtIndex:
2019-12-26 10:52:51.973891+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - ageAtIndexes:
2019-12-26 10:52:51.973989+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - indexInAgeOfObject:
2019-12-26 10:52:51.974202+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - enumeratorOfAge
2019-12-26 10:52:51.974378+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - memberOfAge:
2019-12-26 10:52:51.974552+0800 KVC&KVO[1368:47095] +[Person accessInstanceVariablesDirectly]
2019-12-26 10:52:51.974779+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - insertObject:inAgeAtIndex:
2019-12-26 10:52:51.975018+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - insertAge:atIndexes:
2019-12-26 10:52:51.975243+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - removeObjectFromAgeAtIndex:
2019-12-26 10:52:51.975466+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - removeAgeAtIndexes:
2019-12-26 10:52:51.975685+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - insertObject:inAgeAtIndex:
2019-12-26 10:52:51.975868+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - insertAge:atIndexes:
2019-12-26 10:52:51.976051+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - removeObjectFromAgeAtIndex:
2019-12-26 10:52:51.976241+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - removeAgeAtIndexes:
2019-12-26 10:52:51.976468+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - addAgeObject:
2019-12-26 10:52:51.976682+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - removeAge:
2019-12-26 10:52:51.976888+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - removeAgeObject:
2019-12-26 10:52:51.977102+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - addAge:
2019-12-26 10:52:51.977341+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - setAge:
2019-12-26 10:52:51.977544+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - _setAge:
2019-12-26 10:52:51.977741+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - setIsAge:
2019-12-26 10:52:51.977934+0800 KVC&KVO[1368:47095] +[Person accessInstanceVariablesDirectly]
2019-12-26 10:52:51.978175+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - getAge
2019-12-26 10:52:51.978365+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - age
2019-12-26 10:52:51.978580+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - isAge
2019-12-26 10:52:51.978813+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - _getAge
2019-12-26 10:52:51.979030+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - _age
2019-12-26 10:52:51.979249+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - countOfAge
2019-12-26 10:52:51.979461+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - objectInAgeAtIndex:
2019-12-26 10:52:51.979712+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - ageAtIndexes:
2019-12-26 10:52:51.979951+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - indexInAgeOfObject:
2019-12-26 10:52:51.980183+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - enumeratorOfAge
2019-12-26 10:52:51.980433+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - memberOfAge:
2019-12-26 10:52:51.980666+0800 KVC&KVO[1368:47095] +[Person accessInstanceVariablesDirectly]
2019-12-26 10:52:51.980926+0800 KVC&KVO[1368:47095] -[ViewController observeValueForKeyPath:ofObject:change:context:]
2019-12-26 10:52:51.981215+0800 KVC&KVO[1368:47095] keyPath = age
2019-12-26 10:52:51.981500+0800 KVC&KVO[1368:47095] change = {
kind = 1;
new = 300;
old = 0;
}
2019-12-26 10:52:51.981763+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - _dynamicContextEvaluation:patternString:
2019-12-26 10:52:51.982011+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - descriptionWithLocale:
2019-12-26 10:52:51.982258+0800 KVC&KVO[1368:47095] 观察后对象 :<Person: 0x600003894530>
2019-12-26 10:52:51.982467+0800 KVC&KVO[1368:47095] +[Person resolveClassMethod:] - _dynamicContextEvaluation:patternString:
2019-12-26 10:52:51.982684+0800 KVC&KVO[1368:47095] +[Person resolveClassMethod:] - descriptionWithLocale:
2019-12-26 10:52:51.982925+0800 KVC&KVO[1368:47095] 观察后对象类:NSKVONotifying_Person
2019-12-26 10:52:51.983145+0800 KVC&KVO[1368:47095] +[Person resolveClassMethod:] - _dynamicContextEvaluation:patternString:
2019-12-26 10:52:51.983355+0800 KVC&KVO[1368:47095] +[Person resolveClassMethod:] - descriptionWithLocale:
2019-12-26 10:52:51.983568+0800 KVC&KVO[1368:47095] 观察后对象父类 :Person
2019-12-26 10:52:51.983824+0800 KVC&KVO[1368:47095] 观察后所有属性 :(
)
2019-12-26 10:52:51.984010+0800 KVC&KVO[1368:47095] 观察后所有变量 :(
)
2019-12-26 10:52:51.984210+0800 KVC&KVO[1368:47095] 观察后所有方法 :(
class,
dealloc,
"_isKVOA"
)
2019-12-26 10:52:51.984429+0800 KVC&KVO[1368:47095] -[Person methodForSelector:] - setAge:
2019-12-26 10:52:51.984654+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - setAge:
2019-12-26 10:52:51.984875+0800 KVC&KVO[1368:47095] -[Person methodForSelector:] - class
2019-12-26 10:52:51.985099+0800 KVC&KVO[1368:47095] 移除观察后对象 :<Person: 0x600003894530>
2019-12-26 10:52:51.985277+0800 KVC&KVO[1368:47095] 移除观察后对象类:Person
2019-12-26 10:52:51.985466+0800 KVC&KVO[1368:47095] 移除观察后对象父类 :NSObject
2019-12-26 10:52:51.985674+0800 KVC&KVO[1368:47095] 移除观察后所有属性 :(
)
2019-12-26 10:52:51.985875+0800 KVC&KVO[1368:47095] 移除观察后所有变量 :(
"_age"
)
2019-12-26 10:52:51.986070+0800 KVC&KVO[1368:47095] 移除观察后所有方法 :(
"doesNotRecognizeSelector:",
"forwardingTargetForSelector:",
"methodForSelector:",
"forwardInvocation:"
)
2019-12-26 10:52:51.986264+0800 KVC&KVO[1368:47095] -[Person methodForSelector:] - setAge:
2019-12-26 10:52:51.986515+0800 KVC&KVO[1368:47095] +[Person resolveInstanceMethod:] - setAge:
2019-12-26 10:52:51.986763+0800 KVC&KVO[1368:47095] -[Person methodForSelector:] - class
结果分析: KVO并不一定会重写setter方法,前提是父类中存在setter方法则会重写,不存在,则会动态解析setter方法,再获取实例并在改变实例的前后注入willChangeValueForKey:和didChangeValueForKey:方法来触发KVO
最后总结-KVO实现原理:
当一个NSObejct对象或者其子类对象(暂且记为XXX)某个属性(属性暂且记为m)被观察时,会派生一个子类NSKVONotifying_XXX,此时父类XXX的isa指针会指向其派生类NSKVONotifying_XXX,该子类会先检查父类XXX是否存在setM:方法,如果存在,则会重写setM:方法,并注入willChangeValueForKey:和didChangeValueForKey:方法来触发KVO .如果不存在setM:方法,则不会添加该方法,而是动态解析setM:方法,再获取父类实例变量,并且在赋值实例变量的前后注入willChangeValueForKey:和didChangeValueForKey:方法来触发KVO. 子类NSKVONotifying_XXX重写的方法还有class,和dealloc方法。子类NSKVONotifying_XXX同时增加了一个_isKVOA方法. 当移除观察时,被观察的对象isa又重新指向自身类XXX
如有不正确的的地方(主要是:是否重写setter方法),欢迎进行交流!