一.原理
KVO是系统用运行时机制给需要观察的对象增加一个子类的方法实现的,如果一个对象被观察,系统会给他增加以各子类,重写它的set方法,从而实现观察对象属性变化的功能,为了使外部看不出来,系统还重写了被观察对象的-class,-isEqual:等方法。
二.自定义KVO(仅实现key,未实现keyPath)
1.这个方式是给要观察的类添加一个子类
- (Class)achiveNewSubClass:(NSString *)className
withCurrentClass:(Class)currentClass
{
Class newClass = objc_allocateClassPair([self class],
className.UTF8String,
0);
// 可以在这里给类添加成员变量等
objc_registerClassPair(newClass);
return newClass;
}
2.这个是重写被观察对象的set方法
void kvo_setter(id self, SEL selecter, id newValue) {
KHHandle handle = objc_getAssociatedObject(self,
@"123".UTF8String);
NSString * key = achiveKeyPath(selecter);
struct objc_super superclazz = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self))
};
id oldValue = [self valueForKey:key];
void (* objc_msgSendToSuper)(struct objc_super * ,SEL , id) = (void *)objc_msgSendSuper;
// 在这里调取运行时创建的子类的父类的set方法赋值
objc_msgSendToSuper(&superclazz, selecter, newValue);
NSDictionary * dict = @{KHKeyValueNew : newValue ?: @"",
KHKeyValueOld : oldValue ?: @"",
};
handle(dict);
}
3.这个是提供给外部调用的方法
- (void)KH_addObserverWithKeyPath:(NSString *)keyPath
withBlock:(KHHandle)handle
{
Class currentClass = [self class];
NSString * className = NSStringFromClass(currentClass);
if ([className hasPrefix:CLASS_PREFIX]) return;
className = [CLASS_PREFIX stringByAppendingString:className];
Class subClass =NSClassFromString(className);
if (subClass) return;
objc_setAssociatedObject(self,
@"123".UTF8String,
handle,
OBJC_ASSOCIATION_COPY_NONATOMIC);
subClass = [self achiveNewSubClass:className
withCurrentClass:currentClass];
object_setClass(self, subClass);
SEL selecter = NSSelectorFromString(achiveMethodName(keyPath));
Method me = class_getInstanceMethod(currentClass, selecter);
const char * types = method_getTypeEncoding(me);
BOOL isSuccess = class_addMethod(subClass, selecter, (IMP)kvo_setter, types);
NSLog(@"%d",isSuccess);
}
网友评论