kvo底层实现 以及自己实现kvo

2021-05-12 18:05:20 浏览数 (1)

调用 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil

系统为我们动态添加了一个NSKVONotifying_ 类名的类,因为我们改变对象属性的值是通过setter方法实现了,所以很明显是系统动态生成的NSKVONotifying_ZJPerson类重写了setter方法。

发现方法实现变了,内部调用了系统Foundation框架下的_NSSetObjectValueAndNotify方法。

_NSSetObjectValueAndNotify函数内部实现过程如下

1. `-[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:]:

2. -[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:usingBlock:]:

3. [ZJPerson setName:];

4. `NSKeyValueDidChange:

5. `NSKeyValueNotifyObserver:

6. - (void)observeValueForKeyPath:ofObject:change:context

简化成OC的伪代码大致如下:

- (void)setName:(NSString *)name{

_NSSetObjectValueAndNotify();

}

void _NSSetObjectValueAndNotify {

[self willChangeValueForKey:@"name"];

[super setName:name];

[self didChangeValueForKey:@"name"];

}

- (void)didChangeValueForKey:(NSString *)key{

[observe observeValueForKeyPath:key ofObject:self change:nil context:nil];

}

可以利用runtime方法打印一下方法列表:

unsigned int count;

Method *methods = class_copyMethodList(object_getClass(self.person), &count);

for (NSInteger index = 0; index < count; index ) {

   Method method = methods[index];

   NSString *methodStr = NSStringFromSelector(method_getName(method));

NSLog(@"%@n", methodStr);

}

2018-05-20 08:57:07.883400 0800 KVO[35888:3218908] setName:

2018-05-20 08:57:07.883571 0800 KVO[35888:3218908] class

2018-05-20 08:57:07.883676 0800 KVO[35888:3218908] dealloc

2018-05-20 08:57:07.883793 0800 KVO[35888:3218908] _isKVOA

简单分析下重写这些方法的作用:

class:重写这个方法,是为了伪装苹果自动为我们生成的中间类。

dealloc:应该是处理对象销毁之前的一些收尾工作

_isKVOA:告诉系统使用了kvo

自己动手写一个KVO

KVO底层实现还是很复杂的,下面我只是简单的写下实现过程:

  • 因为它是一个非正式协议,给NSObject新建一个Category,NSObject kvo.h,添加监听方法:

.h

#import <Foundation/Foundation.h>

@interface NSObject (kvo)

- (void)zj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

@end

.m

#import "NSObject kvo.h"

#import <objc/runtime.h>

#import <objc/message.h>

@implementation NSObject (kvo)

- (void)zj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{

//动态添加一个类

    NSString *originClassName = NSStringFromClass([self class]);

    NSString *newClassName = [@"ZJKVO_" stringByAppendingString:originClassName];

const char *newName = [newClassName UTF8String];

// 继承自当前类,创建一个子类

    Class kvoClass = objc_allocateClassPair([self class], newName, 0);

// 添加setter方法

class_addMethod(kvoClass, @selector(setName:), (IMP)setName, "v@:@");

//注册新添加的这个类

objc_registerClassPair(kvoClass);

// 修改isa指针,由ZJPerson指向ZJKVO_Person

object_setClass(self, kvoClass);

// 保存观察者属性到当前类中

objc_setAssociatedObject(self, (__bridge const void *)@"observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

#pragma mark - 重写父类方法

void setName(id self, SEL _cmd, NSString *name) {

// 保存当前KVO的类

    Class kvoClass = [self class];

// 将self的isa指针指向父类ZJPerson,调用父类setter方法

object_setClass(self, class_getSuperclass([self class]));

// 调用父类setter方法,重新复制

objc_msgSend(self, @selector(setName:), name);

// 取出ZJKVO_Person观察者

    id objc = objc_getAssociatedObject(self, (__bridge const void *)@"observer");

// 通知观察者,执行通知方法

objc_msgSend(objc, @selector(observeValueForKeyPath:ofObject:change:context:), name, self, nil, name);

// 重新修改为ZJKVO_Person类

object_setClass(self, kvoClass);

}

0 人点赞