KVO的底层原理和自实现KVO
2018-07-27 本文已影响0人
强子_4216
一、KVO的概述
KVO : Key-Value Observing(观察者设计模式)
当被观察对象的值改变时,观察者就会受到通知,这种模式有利于两个类间的解耦合,尤其是对于业务逻辑与视图控制 这两个功能的解耦合。
二、KVO的实现原理
- 1、KVO是基于runtime机制实现的;
- 2、当某个类(Person)的属性被观察时,系统会自动的派生一个该类的子类(NSKVONotifying_Person)
- 3、为该子类添加setter方法;
- 4、消息转发,子类(NSKVONotifying_Person)--> 父类(Person);
- 备注:苹果粑粑还悄悄的重写了该类的class方法,从而让我们感知不到子类的存在;
三、自实现KVO(观察者模式)
首先看下系统的KVO
//给self.Person属性添加观察者
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
//通知观察者
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if ([keyPath isEqualToString:@"name"]) {
NSLog(@"通知了观察者,被观察的值改变了");
}
}
下面我们自己实现KVO
话不多说,代码撸起 Demo
//NSObject+CGQKVO.h
#import <Foundation/Foundation.h>
#import <objc/message.h>
typedef void (^QZKVOBlock)(id observer, id keyPath, id newValue, id oldValue);
@interface NSObject (CGQKVO)
/**
添加观察者
@param observer 观察者对象
@param keyPath 需要观察的键值
@param handle 回调block
*/
- (void)qz_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath withHandle:(QZKVOBlock)handle;
/**
移除观察者
@param observer 观察值对象
@param keyPath 观察的键值
*/
-(void)qz_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
@end
//创建一个info类来保存观察的数据
@interface QZ_Info : NSObject
@property (nonatomic, weak) id observer;
@property (nonatomic,copy) NSString *keyPath;
@property (nonatomic,copy) QZKVOBlock handle;
-(instancetype)initWhit:(id)observer keyPath:(NSString *)keyPath handle:(QZKVOBlock)handle;
@end
@implementation QZ_Info
-(instancetype)initWhit:(id)observer keyPath:(NSString *)keyPath handle:(QZKVOBlock)handle{
self = [super init];
if (self) {
_observer = observer;
_keyPath = keyPath;
_handle = handle;
}
return self;
}
@end
//NSObject+CGQKVO.m
#import "NSObject+CGQKVO.h"
static NSString *const QZKVOPrefix = @"QZKVO_";
static NSString *const QZKVOAssicationKey = @"QZKVOAssicationKey";
@implementation NSObject (CGQKVO)
- (void)qz_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath withHandle:(QZKVOBlock)handle{
//检查是否是实例变量(实例变量就抛出异常)
//1.获取当前类
Class superClass = object_getClass(self);
//2.检查是否有setter方法
SEL setterSeletor = NSSelectorFromString(setterFormGetter(keyPath));
Method setterMethod = class_getInstanceMethod(superClass, setterSeletor);
if (!setterMethod) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"这个key:%@,没有setter:方法",keyPath] userInfo:nil];
}
//动态创建子类创建子类
NSString *superClassName = NSStringFromClass(superClass);
Class childClass;
if (![superClassName hasPrefix:QZKVOPrefix]) {
childClass = [self createClassFromSuperClass:superClassName];
//把动态创建的子类指向父类
//isa_swizzling-->黑魔法
object_setClass(self, childClass);
}
//为子类添加setter方法
/**
class_addMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>, <#IMP _Nonnull imp#>, <#const char * _Nullable types#>)
1.class --> 给谁添加
2.SEL --> 方法编号
3.IMP --> 函数指针,指向函数实现
4.types --> 返回值,参数
*/
const char *types = method_getTypeEncoding(setterMethod);
class_addMethod(childClass, setterSeletor, (IMP)QZKVO_Setter, types);
//添加回调,方便调用者处理
QZ_Info *info = [[QZ_Info alloc] initWhit:observer keyPath:keyPath handle:handle];
NSMutableArray *Arr = objc_getAssociatedObject(self, &QZKVOAssicationKey);
if (!Arr) {
Arr = [[NSMutableArray alloc] initWithCapacity:1];
objc_setAssociatedObject(self, &QZKVOAssicationKey, Arr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[Arr addObject:info];
}
-(void)qz_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath{
NSMutableArray *Arr = objc_getAssociatedObject(self, &QZKVOAssicationKey);
QZ_Info *tmp;
for (QZ_Info *info in Arr) {
if ([info.keyPath isEqualToString:keyPath]) {
tmp = info;
break;
}
}
if (tmp) {
[Arr removeObject:tmp];
}
}
/**
通过父类创建子类
@param superClassName 父类类名
@return 子类
*/
- (Class)createClassFromSuperClass:(NSString *)superClassName{
//获取父类
Class superClass = object_getClass(self);
//创建新类
//1.获取子类类名
NSString *childClassName = [QZKVOPrefix stringByAppendingString:NSStringFromClass(superClass)];
//2.检查是否创建过该子类
Class childClass = NSClassFromString(childClassName);
//创建过就直接返回改类
if (childClass) return childClass;
//没创建过就开始创建
/**
1.superClass --> 父类
2.childClassName --> 创建的类名
3.size_t extraBytes -->开辟的内存空间
*/
childClass = objc_allocateClassPair(superClass, childClassName.UTF8String, 0);
/**
添加子类动态添加的class方法
class_addMethod(Class _Nullable __unsafe_unretained cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)
1.class --> 给谁添加
2.SEL --> 方法编号
3.IMP --> 函数指针,指向函数的实现
4.types --> 返回值/参数
*/
Method childClassMethod = class_getInstanceMethod(superClass, @selector(class));//获取父类的class方法
const char *types = method_getTypeEncoding(childClassMethod);
class_addMethod(childClass, @selector(class),(IMP)QZKVO_Class, types);
//注册
objc_registerClassPair(childClass);
return childClass;
}
#pragma mark - 函数区域
static void QZKVO_Setter(id self, SEL _cmd, id newValue){
NSString *setterName = NSStringFromSelector(_cmd);
NSString *getterName = getterFormSetter(setterName);
if (!getterName) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%@没有getter:%@方法",self,getterName] userInfo:nil];
}
//取出旧值
id oldValue = [self valueForKey:getterName];
//即将改变值
[self willChangeValueForKey:getterName];
//消息转发 子类-->父类
/**
objc_msgSendSuper(void * struct objc_super *super, SEL op, ... *)
1.super --> void * struct objc_super 结构体
2.op --> SEL方法编号
*/
void (*qzkvo_megSendSuper)(void *, SEL, id) = (void*)objc_msgSendSuper;
struct objc_super qz_objcSuper = {
/// Specifies an instance of a class.
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self)),
};
qzkvo_megSendSuper(&qz_objcSuper, _cmd, newValue);
//已经改变值
[self didChangeValueForKey:getterName];
NSArray *Arr = objc_getAssociatedObject(self, &QZKVOAssicationKey);
for (QZ_Info *info in Arr) {
if ([info.keyPath isEqualToString:getterName]) {
info.handle(info.observer, info.keyPath, newValue, oldValue);
}
}
}
static Class QZKVO_Class(id self){
return class_getSuperclass(object_getClass(self));
}
//name --> setName:
static NSString *setterFormGetter(NSString * getter){
if (getter.length == 0) return nil;
NSString *firstString = [[getter substringToIndex:1] uppercaseString];
NSString *lastString = [getter substringFromIndex:1];
return [NSString stringWithFormat:@"set%@%@:",firstString,lastString];
}
//setName: --> name
static NSString *getterFormSetter(NSString * setter){
if (setter.length <= 0 || ![setter hasPrefix:@"set"] || ![setter hasSuffix:@":"]) {
return nil;
}
NSRange range = NSMakeRange(3, setter.length-4);
NSString *getterStr = [setter substringWithRange:range];
NSString *firstString = [[getterStr substringToIndex:1] lowercaseString];
return [getterStr stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:firstString];
}
@end
总结
KVO实际上就是观察对象的setter方法,对于没有setter方法对象和不调用setter方法(_name = @"张三")时,值的改变,是无法触发KVO机制的;
KVO机制能够很方便的提供两个对象间的同步(像view和model之间的同步)
自己完成实现KVO后,会对KVO底层有一个较深入的了解,增加了block回调也能增加代码的可读性和可维护性;