盒子
盒子

KVO

感觉原地停留了很久,再不弄点东西都忘了自己是做iOS开发的。早之前就了解过KVO的实现,也知道大概的实现,但是知道归知道,没自己实践过就是在扯淡,实践实践可以加深自己的印象。

#####分析
大家也都大概的了解过KVO的实现,主要分三步:

  • addObserver:forKeyPath方法创建了一个新类
  • isa指针指向新类
  • 在新类里重写set方法
  • 改变值后sendMsg到observeValueForKeyPath

通过强大的Xcode我们可以看到

old.png

我们断点走到这之后,看到isa指向Person,然后我们在走一步
new.png
看到此时isa指向了一个NSKVONotifying_Person的类,对的这个类就是系统为我们创建的。我们知道了大概的过程,就来模拟一下实现吧。

#####手动模拟实现KVO
这个需要我们对OC的运行时(runtime)了解点。
话不多说贴代码(主要模块)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
- (void)XL_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context{

//1.给新类起个名,可以随意
NSString * oldClassName = NSStringFromClass([self class]);
NSString * newName = [@"XLKVO_" stringByAppendingString:oldClassName];
const char * newClassName = [newName UTF8String];
//2.创建新类
Class newClass = objc_allocateClassPair([self class], newClassName, 0);
//3.添加方法
/*
"v@:@"这个需要解释一下,官方示例v@:无参数无返回值函数,v@:@无返回值有参数
可以option class_addMethod方法看一看
这个还有个注意点,添加方法之前应该先判断这个类有没有这个方法
*/
class_addMethod(newClass, @selector(setName:), (IMP)setName, "v@:@");
//4.注册新类
objc_registerClassPair(newClass);

//5.isa->newClass
object_setClass(self, newClass);

objc_setAssociatedObject(self, "objc", observer, OBJC_ASSOCIATION_ASSIGN);

}
/*
函数的前两个参数需要说明一下,
函数默认是有两个参数,调用对象,一个方法编号
Objective-C 2.0运行时系统 书中有讲到
*/
void setName(id self, SEL _cmd,NSString *newName){

//保存当前类型
id class = [self class];
//改变当前对象指向父类
object_setClass(self, class_getSuperclass([self class]));
//调用父类的setName方法
objc_msgSend(self, @selector(setName:),newName);
//拿出观察者
id observer = objc_getAssociatedObject(self, "objc");
//通知外界
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),self,@"name",@{@"new":newName},nil);
//改回子类类型
object_setClass(self, class);
}

附:Demo地址+运行时那本书

支持一下
扫一扫,支持ddSoul