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 ++ ;
}
我們可以看到列印