通过手动实现KVO,对KVO底层原理有一定认识。
KVO只要是通过监听set方法,从而实现对该对象的监听。
要监听set方法,有两种实现方式,第一就是使用分类,重写set方法,但是这样就会覆盖父类的set方法,所以不可行,pass掉。
第二就是使用子类,把父类的isa指针改为子类。然后调用父类色set方法,最后调用回调方法,该方案可行。
首先是注册监听,在调用监听方法的时候,会动态实现子类,把observer保存到子类的属性中(弱引用weak类型,不能使用strong,会造成循环引用),并且把类型为父类的self 的 isa指针更改为子类。在调用set方法的时候,首先需要调用父类的set方法(通过把isa指针改为父类,调用父类的set方法),然后再调用监听回调方法(把父类色isa指针改回子类,取出observer,通过observer调用监听回调方法)。
废话不多说,直接上代码。
首先是结构目录,其中NSObject+LLKVO是NSObject的子类,作用是动态实现观察对象(比如Person)的子类。
NSObject+LLKVO的代码
#import <Foundation/Foundation.h> @interface NSObject (LLKVO) - (void)LL_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context; @end #import "NSObject+LLKVO.h" #import <objc/message.h> static NSString *OBSERVER = @"observer"; @implementation NSObject (LLKVO) - (void)LL_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context { //1.创建一个类 NSString *oldClassName = NSStringFromClass(self.class); NSString *newClassName = [@"LLKVO_" stringByAppendingString:oldClassName]; Class myClass = objc_allocateClassPair(self.class, newClassName.UTF8String, 0); //注册类 objc_registerClassPair(myClass); //2.重写setName方法 /** *class 给哪个类加方法 *sel 方法编号 *imp 方法实现(函数指针) *type 返回值类型 */ class_addMethod(myClass, @selector(setName:), (IMP)setName, "[email protected]:@"); //3.修改isa指针 object_setClass(self, myClass); //4.将数据观察者保存到当前对象 objc_setAssociatedObject(self, OBSERVER.UTF8String, observer, OBJC_ASSOCIATION_ASSIGN); } void setName(id self,SEL _cmd,NSString *newName) { //改为父类的类型,调用父类的set方法 Class newClass = [self class]; object_setClass(self, class_getSuperclass(newClass)); void (* action1)(id,SEL,NSString *) = (void (*) (id,SEL,NSString *))objc_msgSend; action1(self,@selector(setName:),newName); //改为子类 object_setClass(self, newClass); //取出观察者 id observer = objc_getAssociatedObject(self, OBSERVER.UTF8String); if (observer) { void (* action)(id,SEL,NSString *,id,NSDictionary *,id) = (void (*) (id,SEL,NSString *,id,NSDictionary *,id)) objc_msgSend; action(observer,@selector(LL_observeValueForKeyPath:ofObject:change:context:),@"name",self,@{@"kind":@"1",@"new":newName},nil); } }
Person的代码,很简单就定义了一个name属性,重写了下set方法
#import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic, copy) NSString *name; @end #import "Person.h" @implementation Person - (void)setName:(NSString *)name { _name = name; NSLog(@"我重写了set方法"); } @end
ViewController中的代码
#import "ViewController.h" #import "NSObject+LLKVO.h" #import "Person.h" @interface ViewController () @property (nonatomic, strong) Person *person; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. self.person = [Person new]; [self.person LL_addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:nil]; } - (void)LL_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSLog(@"%@",change); } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { static NSInteger a = 0; self.person.name = [NSString stringWithFormat:@"name -- %tu",++a]; } @end
Log如下:
2018-12-04 11:43:57.742679+0800 KVO原理浅析[1494:324838] 我重写了set方法 2018-12-04 11:43:57.743071+0800 KVO原理浅析[1494:324838] { kind = 1; new = "name -- 1"; }
通过自己实现KVO,明白了KVO的底层原理,苹果底层肯定做的更加详细,功能更加多,但是最基本的思想应该是一致的。
网上肯定有很多大神写的比我详细,底层原理剖析的更加彻底,仅以该博客记录自己对KVO的实现和理解,以后忘记了翻一下也可以快速想起。
原文地址:https://www.cnblogs.com/funny11/p/10063278.html
时间: 2024-11-12 07:11:39