上一篇讲到了KVO的底层实现原理,这一篇说一下怎么去自定义KVO
首先创建一个NSObject的category,自己写一个addobserver方法
image.png
1.获取当前类名,为新创建的类添加名字
image.png
2.使用runtime添加类(参数:父类, 类名)
image.png
3.注册类
image.png
4.动态修改self的类型
image.png
5.重写set方法->给子类对象添加set方法
image.png
6.重写set方法,并给父类发送set方法给属性赋值
image.png
7.为当前类绑定一个observer属性,方便在setName中获取
8.为当前类绑定一个keyPath,方便在setName中获取
image.png
9.在setCustomMethod中获取observer,发送observeValueForKeyPath方法
image.png
自定义完成
接下来看运行结果
image.png
/*********************完整代码****************/
NSObject+KVO.h中
@interface NSObject (KVO)
- (void)RH_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
@end
NSObject+KVO.m中
#import "NSObject+KVO.h"
#import <objc/message.h>
@implementation NSObject (KVO)
-(void)RH_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
// 1.获取当前类的名字
NSString *oldClassName = NSStringFromClass([self class]);
NSString *newClassName = [@"RHNotifying_" stringByAppendingString: oldClassName];
//2.使用runtime添加类(参数:父类, 类名)
Class myClass = objc_allocateClassPair([self class], newClassName.UTF8String, 0);
//3.注册类
objc_registerClassPair(myClass);
//4.动态修改self的类型
object_setClass(self, myClass);
//5.重写set方法->给子类对象添加set方法
//获取方法名
NSString *methodName = [@"set" stringByAppendingString:keyPath.capitalizedString];
methodName = [methodName stringByAppendingString:@":"]; //因为有参数,所以方法名要加:
SEL setMethod = NSSelectorFromString(methodName);
class_addMethod(myClass, setMethod, (IMP)setCustomMethod, "v@:@");
//6.为当前类绑定一个observer属性,方便在setName中获取
objc_setAssociatedObject(self, (__bridge const void *)@"oberver", observer, OBJC_ASSOCIATION_ASSIGN);
//7.为当前类绑定一个keyPath,方便在setName中获取
objc_setAssociatedObject(self, (__bridge const void *)@"keyPath", keyPath, OBJC_ASSOCIATION_ASSIGN);
}
void setCustomMethod(id self, SEL _cmd, NSString *new){
//调用父类的set方法给name赋值
struct objc_super person = {
self,
class_getSuperclass([self class])
};
// 修改父类的属性
objc_msgSendSuper(&person, _cmd, new);
//获取observer,发送observeValueForKeyPath方法
id observer = objc_getAssociatedObject(self, @"oberver");
id keyPath = objc_getAssociatedObject(self, @"keyPath");
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),keyPath,self, @{keyPath: new}, nil);
}
@end
网友评论