iOS开发:KVO底层实现
2018-04-11 本文已影响23人
Jason_hzb
KVO底层原理
Person * p =[[Person alloc] init];
[p addObserver:self forKeyPath:@"age" options: NSKeyValueObservingOptionOld|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 方法.
123.png
知道原理,我们就可以自己来写一个自己的KVO验证一下
一、模仿系统给NSObejct添加一个分类,具有添加自己的观察者的能力
//
// NSObject+HZB_KVO.h
// KVC-KVO
//
// Created by Jason on 2018/4/11.
// Copyright © 2018年 hzb. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (HZB_KVO)
- (void)Hzb_addObserver:(NSObject *_Nonnull)observer forKeyPath:(NSString *_Nonnull)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
@end
//
// NSObject+HZB_KVO.m
// KVC-KVO
//
// Created by Jason on 2018/4/11.
// Copyright © 2018年 hzb. All rights reserved.
//
#import "NSObject+HZB_KVO.h"
#import "HZB_KVONotifying_Person.h"
#import <objc/runtime.h>
@implementation NSObject (HZB_KVO)
- (void)Hzb_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
// 修改isa指针(runtime) 系统的NSKVONotifying_Person这个类是动态生成的,我们直接手动创建
object_setClass(self, [HZB_KVONotifying_Person class]);
// 给对象动态添加属性.目的是保存observer,好在set方法里面拿到,调用Hzb_addObserver:forKeyPath:options:context:这个方法
objc_setAssociatedObject(self, (__bridge const void *)(keyPath), observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
二、重写HZB_KVONotifying_Person set方法
//
// HZB_KVONotifying_Person.h
// KVC-KVO
//
// Created by Jason on 2018/4/11.
// Copyright © 2018年 hzb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Person.h"
@interface HZB_KVONotifying_Person : Person
@end
//
// HZB_KVONotifying_Person.m
// KVC-KVO
//
// Created by Jason on 2018/4/11.
// Copyright © 2018年 hzb. All rights reserved.
//
#import "HZB_KVONotifying_Person.h"
#import "NSObject+HZB_KVO.h"
#import <objc/runtime.h>
@implementation HZB_KVONotifying_Person
- (void)setAge:(NSInteger)age {
id observer = objc_getAssociatedObject(self, @"age");
if (observer && [observer respondsToSelector:@selector(Hzb_addObserver:forKeyPath:options:context:)]) {
[observer Hzb_addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
}
[super setAge:age];
}
@end
三、使用一下
//
// KVOImplementVC.h
// KVC-KVO
//
// Created by Jason on 2018/4/11.
// Copyright © 2018年 hzb. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface KVOImplementVC : UIViewController
@end
//
// KVOImplementVC.m
// KVC-KVO
//
// Created by Jason on 2018/4/11.
// Copyright © 2018年 hzb. All rights reserved.
//
#import "KVOImplementVC.h"
#import "Person.h"
#import "NSObject+HZB_KVO.h"
@interface KVOImplementVC ()
@property (nonatomic,strong) Person *p;
@end
@implementation KVOImplementVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
/**********************KVO底层实现***********************/
_p = [[Person alloc] init];
[_p Hzb_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}
#pragma mark - KVO底层实现
- (void)Hzb_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
NSLog(@"自己实现的KVO");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
_p.age++;
}
- (void)dealloc {
[_p removeObserver:self forKeyPath:@"age"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
输出结果:
屏幕快照 2018-04-11 16.48.55.png