探讨KVO(OC)底层实现原理(一)

参考官方文档(developer.apple.com/library/arc…html

image.png

KVO(Key-Value Observing)键值观察,是观察者模式的一种衍生。基本思想就是:经过KVO接口方法,给对象属性添加观察,并指定监听观察者,被观察对象的属性发生变化时,能够触发观察者实现的KVO接口方法,来自动通知监听观察者

  • KVO也是NSObject的一种非正式协议,凡是继承了NSObject的对象均可以使用KVO. KVO提供了一种机制,该机制容许将其余对象的特定属性的更改通知对象。iOS 应用程序MVC架构模式中模型层和控制器层之间的通讯会常常用到KVO。在OS X中,控制器层绑定技术(Cocoa Bingdings)在很大程度上依赖于键值观察.
  • 控制器对象一般观察模型对象的属性,而视图对象经过控制器观察模型对象的属性。
  • 模型对象能够观察其余模型对象(一般用于肯定从属值什么时候更改),甚至能够观察自身(再次肯定从属值什么时候更改)

来看下官方文档,KVO实现细节:ios

KVO实现细节

image.png
自动KVO使用一种称为isa-swizzling的技术实现的.isa指针指向维护分发表的对象类。 该分发表实质上包含指向该类实现的方法的指针以及其余数据.在为对象的属性注册观察者时,将修改观察对象的isa指针,指向中间类(具体使用了runtime动态机制生成了其子类,并重写dealloc和class方法,增长_isKVOA方法)而不是真实类(该对象所属类)。 这样,isa指针的值不必定反映实例的实际类.所以Apple也建议永远不该依靠isa指针来肯定类的类型。 相反,应该使用class方法来肯定对象实例的类

接下来咱们用代码深刻探讨下KVO到底作了些什么?上代码macos

#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
复制代码

运行结果以下:bash

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的说法.app

  • 2.Person类并无age属性,添加KVO观察后,使用了动态方法解析,首先keyPathsForValuesAffectingAge(这里的Age就是咱们的age属性)这个方法解析经过(由于并无走消息转发流程,此方法主要来指明age是依赖哪些键,能够跟随依赖键更新而更新age自身)ui

  • 3.动态方法解析keyPathsForValuesAffectingAge成功后,而后调用automaticallyNotifiesObserversForKey:和 automaticallyNotifiesObserversOfAge是否容许Age这个key自动触发KVO(这里默认都是返回YES容许的),若是返回YES,而且option没有指定NSKeyValueObservingOptionInitial(由于这个并不会被拦截),则开始进行KVC对属性age进行处理,不然,直接返回,没有进行KVC的必要了(这里你能够重写automaticallyNotifiesObserversForKey:返回NO 或者 automaticallyNotifiesObserversOfAge返回NO,它确实中止了KVC流程)atom

  • 4.若是容许对age属性自动KVO,那么接下来就对age属性开始执行KVC流程 (注意option指定NSKeyValueObservingOptionInitial,则会抛出NSUnknownKeyException崩溃,由于KVC首先走age属性的getter流程,发现没有getter一类方法,也没有实例变量,会动态解析生成getPrimitiveAge和primitiveAge方法,但同时并无生成age这个实例变量,因此Peson类没有age这个key致使崩溃)lua

  • 5.KVC首先走age属性的setter流程(注意option指定NSKeyValueObservingOptionInitial则会先走getter流程),发现没有setter一类的存取方法,就去调用accessInstanceVariablesDirectly,查看是否容许直接获取实例变量(这里是容许),若是返回NO不容许,则会去动态解析setPrimitiveAge:,getPrimitiveAge 和 primitiveAge 方法,保证可以正确处理KVO. 注意此时KVC对age进行取值或者设值操做 都会抛出valueForUndefinedKey异常.同理接着顺序处理age属性的getter流程,以及age集合属性处理流程spa

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
复制代码

image.png

运行结果分析: 咱们能够看到_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方法。

image.png

对象类中setter方法确实不是必须的.那么问题又来了,到底是什么触发的KVO? 官方文档中手动KVO有这么一段:

image.png
文档中,Apple 只是经过例子告诉咱们,设值以前使用willChangeValueForKey:和设值以后使用 didChangeValueForKey:就能够发送通知了.那么咱们依葫芦画瓢:

#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通知了,中间直接赋值操做无关紧要)

image.png
到这里咱们能够知道触发KVO的两个核心方法了,可是仍是有一个疑惑,就是怎么实现的呢?这里有两种猜测:

  • 1.对象添加注册观察时,采用了派生类机制,父类isa指针指向派生类,并重写父类setter方法,并加入了willChangeValueForKey:和didChangeValueForKey:方法
  • 2.对象添加注册观察时,使用了runtime动态机制,在改变实例变量的先后(也即赋值操做先后)分别注入了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
复制代码

image.png

打印有点多,具体看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方法),猜想用来标记对象是否被添加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
复制代码

image.png

运行结果分析: 这里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执行流程):

image.png

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)某个属性或者某个成员变量(上面已经验证能够对成员变量进行KVO,不必定要求是属性,这里暂且成员变量记为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方法),欢迎进行交流!

相关文章
相关标签/搜索