KVO原理:调用监听对象属性的方法,
动态
创建一个继承自该对象所属类的子类,然后重写该属性的setter方法,在setter方法类调用willChangeValueForKey:
和didChangeValueForKey:
方法来触发observeValueForKeyPath:ofObject:change:context:
方法。
大概看了一下,大多数手动实现KVO都并没有做到真正动态创建类,而是手动。本文主要是动态创建继承类
及动态重写setter方法
。
直接贴代码吧,注释都在代码里面。
首先创建NSObject分类NSObject+XKVO
暴露接口:
.h
#import <Foundation/Foundation.h>
@interface NSObject (XKVO)
- (void)xl_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context ;
@end
.m
#import "NSObject+XKVO.h"
#import <objc/runtime.h>
#import <objc/message.h>
@implementation NSObject (XKVO)
// 属性:用于保存监听对象
static char *kObserver = "kObserver";
- (void)setObserver:(id)observer {
objc_setAssociatedObject(self, kObserver, observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)observer {
return objc_getAssociatedObject(self, kObserver);
}
void setterMethod(id self, SEL _cmd, id obj) {
/* 根据_cmd和属性列表获取属性名和属性对应的old value数据 */
NSString *name = NSStringFromSelector(_cmd);
NSString *keyPath = [[name lowercaseString] substringFromIndex:3];
keyPath = [keyPath substringToIndex:keyPath.length-1];
id oldValue ;
unsigned int count ;
Ivar *ivarlist = class_copyIvarList([self superclass], &count);
for (int i = 0; i < count; i++) {
Ivar thisivar = ivarlist[i];
NSString *name = [NSString stringWithUTF8String:ivar_getName(thisivar)];
NSString *properName = [name substringFromIndex:1];
name = [[name lowercaseString] substringFromIndex:1];
BOOL isEqual = [name isEqualToString:keyPath];
if (isEqual) {
keyPath = properName;
oldValue = [self valueForKey:keyPath];
break;
}
}
/* 调用父类的setter方法,保证设置数据不被干扰 */
Method sm = class_getInstanceMethod([self superclass], _cmd);
IMP imp = method_getImplementation(sm);
imp(self, _cmd, obj);
id obs = objc_getAssociatedObject(self, kObserver);
if (obs) {
/* 调用监听对象实现的方法 */
objc_msgSend(obs, @selector(observeValueForKeyPath:ofObject:change:context:), keyPath, self, @{@"NSKeyValueObservingOptionNew":obj, @"NSKeyValueObservingOptionOld":oldValue}, nil);
}
}
- (void)xl_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context {
/* 保存监听对象 */
self.observer = observer;
/* 动态创建子类 */
Class NSObjectKVO = objc_allocateClassPair([self class], "NSObjectKVO", 0);
/* 获取setter方法名 */
NSString*setterName=[NSString stringWithFormat:@"set%@%@:",[[keyPath substringToIndex:1] uppercaseString],[keyPath substringFromIndex:1]];
/* 为子类动态添加方法 */
BOOL success = class_addMethod(NSObjectKVO, NSSelectorFromString(setterName), (IMP)setterMethod, "V@:");
if (success) {
/* 将当前对象指向新建的子类 */
object_setClass(self, NSObjectKVO);
}else{
}
}
@end