KVC/KVO实现原理
source link: https://www.tuicool.com/articles/bi67Fzi
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
一、kvo实现原理
一个对象的属性被观察时系统动态创建了一个子类,并且改变了原有对象的 isa
指针指向,指向动态创建的子类,子类中重写了被观察属性的 set
方法,在使用点方法和 set
方法给属性赋值时,最终调用的是子类中的 set
方法。 注意:查看 isa
指针指向,如果断点执行过程中 isa
指针没有变化, 需要关闭 xcode
重新打开
相关代码:在 addObserver
处设置断点观察对象 isa
指针变化:
- (void)viewDidLoad { [super viewDidLoad]; People *p = [[People alloc] init]; [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil]; p.name = @"123"; } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey> *)change context:(void *)context{ NSLog(@"%@",change); }
通过断点观察 isa
指针的指向 被观察前 isa
指针指向的是原始类如图:
执行代码被观察后指针指向的是 NSKVONotifying_People
类:
二、自定义KVO
创建一个分类新增一个方法 HBaddObserver
,在方法中创建子类注册并指向子类,再为子类添加 set
方法既可。
主要使用函数:1、创建一个子类
objc_allocateClassPair(Class _Nullable superclass, const char * _Nonnull name, size_t extraBytes) superclass:设置新类的父类 name:新类名称 extraBytes:额外字节数设置为0
2、注册该类
objc_registerClassPair(Class _Nonnull cls) cls:当前要注册的类,注册后才可以使用
3、设置当前对象指向其他类
object_setClass(id _Nullable obj, Class _Nonnull cls) obj:要设置的对象 cls:指向的类
4、动态添加一个方法
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types) cls:设置添加方法对应的类 name:选择子(选择器)名称,描述了方法的格式,并不会指向方法 imp:函数名称(函数指针),和选择子一一对应,指向方法实现的地址
通过分类添加新的观察者添加方法:
-(void)HBaddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{ NSString *oldName = NSStringFromClass(self.class); NSString *newName = [@"HBKVO_" stringByAppendingString:oldName]; //1、创建一个子类 Class newClass = objc_allocateClassPair(self.class, newName.UTF8String, 0); //2、注册该类 objc_registerClassPair(newClass); //3、指向子类 object_setClass(self, newClass); //4、动态添加一个方法 NSString *first = [keyPath substringWithRange:NSMakeRange(0, 1)]; NSString *other = [keyPath substringFromIndex:1]; NSString *setName = [NSString stringWithFormat:@"set%@%@:",first.uppercaseString,other];//设置一个属性名首字母大写的方法 Method method = class_getInstanceMethod(self.class, sel_registerName(setName.UTF8String)); const char *types = method_getTypeEncoding(method); class_addMethod(newClass, sel_registerName(setName.UTF8String), (IMP)setValue, types); //class_addMethod(newClass, sel_registerName(setMethod.UTF8String), (IMP)setName, "v@:@"); //设置关联数据 //获取元类旧值使用 objc_setAssociatedObject(self, "keyPath", keyPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //设置新值的时候使用 objc_setAssociatedObject(self, "setName", setName, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //通知值变化 objc_setAssociatedObject(self, "observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //传进来的内容需要回传 objc_setAssociatedObject(self, "context", (__bridge id _Nullable)(context), OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
设置属性值的新调用方法:
void setValue(id self,SEL _cmd,NSString *newValue){ NSLog(@"newValue:%@",newValue); NSString *keyPath = objc_getAssociatedObject(self, "keyPath"); NSString *setName = objc_getAssociatedObject(self, "setName"); id observer = objc_getAssociatedObject(self, "observer"); id context = objc_getAssociatedObject(self, "context"); //存储新类 Class newClass = [self class]; //指向父类获取旧值 object_setClass(self, class_getSuperclass(newClass)); NSString *oldValue = objc_msgSend(self,sel_registerName(keyPath.UTF8String)); //对原始类属性或成员变量复制 objc_msgSend(self, sel_registerName(setName.UTF8String),newValue); NSMutableDictionary *change = [NSMutableDictionary dictionary]; if (oldValue) { change[NSKeyValueChangeOldKey] = oldValue; } if (newValue) { change[NSKeyValueChangeNewKey] = newValue; } //调用observer的回调方法 objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),keyPath,observer,change,context); //操作完成后指回动态创建的新类 object_setClass(self, newClass); }
通过断点观察 isa
指针的指向 被观察前 isa
指针指向的是原始类如图:
执行代码被观察后指针指向的是 HBKVO_Person
类:
1、主控制器代码如下:
#import "ViewController.h" #import <objc> #import "Person.h" #import "NSObject+HBKVO.h" @interface ViewController (){ Person *p; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; p = [[Person alloc] init]; [p HBaddObserver:self forKeyPath:@"Name" options:NSKeyValueObservingOptionNew context:nil]; } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey> *)change context:(void *)context{ NSLog(@"change:%@",change); } -(void)touchesBegan:(NSSet<UITouch> *)touches withEvent:(UIEvent *)event{ static int num = 0; p.Name = [NSString stringWithFormat:@"%d",num++]; } @end
2、 person
类头文件代码( .m
中无相关代码):
#import <Foundation> NS_ASSUME_NONNULL_BEGIN @interface Person : NSObject @property (nonatomic,strong)NSString *Name; @end NS_ASSUME_NONNULL_END
3、自定义 KVO
相关代码( NSObject
分类):
NSObject+HBKVO.h
#import <Foundation> NS_ASSUME_NONNULL_BEGIN @interface NSObject (HBKVO) - (void)HBaddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context; @end NS_ASSUME_NONNULL_END
NSObject+HBKVO.m
#import "NSObject+HBKVO.h" #import <objc> @implementation NSObject (HBKVO) -(void)HBaddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{ NSString *oldName = NSStringFromClass(self.class); NSString *newName = [@"HBKVO_" stringByAppendingString:oldName]; //1、创建一个子类 Class newClass = objc_allocateClassPair(self.class, newName.UTF8String, 0); //2、注册该类 objc_registerClassPair(newClass); //3、指向子类 object_setClass(self, newClass); //4、动态添加一个方法 NSString *first = [keyPath substringWithRange:NSMakeRange(0, 1)]; NSString *other = [keyPath substringFromIndex:1]; NSString *setName = [NSString stringWithFormat:@"set%@%@:",first.uppercaseString,other];//设置一个属性名首字母大写的方法 Method method = class_getInstanceMethod(self.class, sel_registerName(setName.UTF8String)); const char *types = method_getTypeEncoding(method); class_addMethod(newClass, sel_registerName(setName.UTF8String), (IMP)setValue, types); //class_addMethod(newClass, sel_registerName(setMethod.UTF8String), (IMP)setName, "v@:@"); //设置关联数据 //获取元类旧值使用 objc_setAssociatedObject(self, "keyPath", keyPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //设置新值的时候使用 objc_setAssociatedObject(self, "setName", setName, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //通知值变化 objc_setAssociatedObject(self, "observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //传进来的内容需要回传 objc_setAssociatedObject(self, "context", (__bridge id _Nullable)(context), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } void setValue(id self,SEL _cmd,NSString *newValue){ NSLog(@"newValue:%@",newValue); NSString *keyPath = objc_getAssociatedObject(self, "keyPath"); NSString *setName = objc_getAssociatedObject(self, "setName"); id observer = objc_getAssociatedObject(self, "observer"); id context = objc_getAssociatedObject(self, "context"); //存储新类 Class newClass = [self class]; //指向父类获取旧值 object_setClass(self, class_getSuperclass(newClass)); NSString *oldValue = objc_msgSend(self,sel_registerName(keyPath.UTF8String)); //对原始类属性或成员变量复制 objc_msgSend(self, sel_registerName(setName.UTF8String),newValue); NSMutableDictionary *change = [NSMutableDictionary dictionary]; if (oldValue) { change[NSKeyValueChangeOldKey] = oldValue; } if (newValue) { change[NSKeyValueChangeNewKey] = newValue; } //调用observer的回调方法 objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),keyPath,observer,change,context); //操作完成后指回动态创建的新类 object_setClass(self, newClass); } @end
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK