利用运行时自定义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);}复制代码