利用运行时自定义KVO
//根据keypath 获取set方法
-(SEL)getNewSelector:(NSString *)selectorName
{
NSString *firstChar = [selectorName substringToIndex:1];
NSString *upFirst = [firstChar uppercaseString];
NSString *otherChar = [selectorName substringFromIndex:1];
NSString *newSelectorName = [NSString stringWithFormat:@"set%@%@:",upFirst,otherChar];
return NSSelectorFromString(newSelectorName);
}
//根据set方法 获取keypath
-(NSString *)getKeypath:(SEL)selector
{
NSString *selectorName = NSStringFromSelector(selector);
selectorName = [selectorName substringFromIndex:3];
NSString *firstChar = [selectorName substringToIndex:1];
NSString *lowFirst = [firstChar lowercaseString];
NSString *otherChar = [selectorName substringFromIndex:1];
NSString *newChar = [NSString stringWithFormat:@"%@%@",lowFirst,otherChar];
return [newChar substringToIndex:newChar.length-1];
}
复制代码
开始监听
-(void)shine_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
{
// 1.动态生成一个派生类
static Class newClass;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
NSString * oldClassName = NSStringFromClass([self class]);
NSString *newClassName = [NSString stringWithFormat:@"Shine_%@",oldClassName];
//创建派生类
newClass = objc_allocateClassPair([self class], [newClassName UTF8String], 0);
//注册派生类
objc_registerClassPair(newClass);
//修改被观察者的isa指针,指向自定义的类
object_setClass(self, newClass);
//动态绑定
objc_setAssociatedObject(self, &Shine_KVO, observer, OBJC_ASSOCIATION_ASSIGN);
});
//2.生成新的set方法
SEL selector = [self getNewSelector:keyPath];
//3.添加新的set方法
class_addMethod(newClass, selector, (IMP)setObj, "v@:@");
}
复制代码
属性更改回调
void setObj(id sf,SEL cd,id value){
NSString *keypath = [sf getKeypath:cd];
id oldValue = [sf valueForKey:keypath];
NSMutableDictionary *change = @{}.mutableCopy;
if (oldValue != nil) {
change[@"old"] = oldValue;
}
if (value != nil) {
change[@"new"] = value;
}
//取出当前类
id class = [sf class];
//指向父类
object_setClass(sf, class_getSuperclass(class));
//向父类发送消息
objc_msgSend(sf, cd ,value);
//获取动态绑定对象
id observe = objc_getAssociatedObject(sf, &Shine_KVO);
//监听回调
objc_msgSend(observe, @selector(shine_observeValueForKeyPath:ofObject:change:),keypath,sf,change);
//修改指向
object_setClass(sf, class);
}
复制代码