KVO底层原理
Person * p =[[Person alloc] init];
[p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
p这个对象一旦添加观察者后,系统会将这个Person这个类的isa指针修改为 NSKVONotifying_Person,NSKVONotifying_Person 这个类是Person类的子类, 这样每次访问p其实访问NSKVONotifying_Person这样类名的p,然后系统会重写p age属性的set方法.在set方法里面调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context这个方法.完了再调用super 方法.

知道原理,我们就可以自己来写一个自己的KVO验证一下
一
模仿系统给NSObejct添加一个分类,具有添加自己的观察者的能力
//
// NSObject+MY_KVO.h
// arc
//
// Created by SGQ on 16/6/2.
// Copyright © 2016年 GQ. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (MY_KVO)
- (void)My_addObserver:(NSObject *_Nonnull)observer forKeyPath:(NSString *_Nonnull)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
@end
实现
//
// NSObject+MY_KVO.m
// arc
//
// Created by SGQ on 16/6/2.
// Copyright © 2016年 GQ. All rights reserved.
//
#import "NSObject+MY_KVO.h"
#import "MY_KVONotifying_Person.h"
#import <objc/runtime.h>
@implementation NSObject (MY_KVO)
-(void)My_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
// 修改isa指针(runtime) 系统的MY_KVONotifying_Person这个类是动态生成的,我们直接手动创建
object_setClass(self, [MY_KVONotifying_Person class]);
// 给对象动态添加属性,之前文章介绍过了.目的是保存observer,好在set方法里面拿到,调用
My_addObserver:forKeyPath:options:context:这个方法
objc_setAssociatedObject(self, (__bridge const void *)(keyPath), observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
二,重写MY_KVONotifying_Person set方法
//
// MY_KVONotifying_Person.m
// arc
//
// Created by SGQ on 16/6/2.
// Copyright © 2016年 GQ. All rights reserved.
//
#import "MY_KVONotifying_Person.h"
#import "NSObject+MY_KVO.h"
#import <objc/runtime.h>
@implementation MY_KVONotifying_Person
- (void)setAge:(int)age{
id observer = objc_getAssociatedObject(self, @"age");
if (observer && [observer respondsToSelector:@selector(My_addObserver:forKeyPath:options:context:)]) {
[observer My_addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
}
[super setAge:age];
}
@end
三.使用一下
#import "ViewController.h"
#import "Person.h"
#import "NSObject+MY_KVO.h"
@interface ViewController ()
@property (nonatomic,strong)Person * p;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person * p =[[Person alloc] init];
[p My_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
_p= p;
}
- (void)My_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
NSLog(@"age++ 自己的KVO");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
_p.age ++ ;
}
我们可以看到打印