美文网首页
iOS 探索KVO底层原理

iOS 探索KVO底层原理

作者: 尤先森 | 来源:发表于2019-03-07 17:57 被阅读0次

KVO底层原理

上一篇文章中说到,KVO监听成员变量无法收到回调。先验证一下是不是对的。

1.创建一个Person类,包含一个公有成员变量age,一个属性变量name

@interface Person : NSObject{
    @public
    int age;
}
@property(strong,nonatomic)NSString * name;

成员变量与属性的区别:
属性会自动生成settergetter方法,成员变量不会。

2.在ViewController中分别监听两个变量。

    self.p = [[Person alloc]init];
        
    [self.p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
    [self.p addObserver:self forKeyPath:@"age" options:(NSKeyValueObservingOptionNew) context:NULL];

3.实现监听回调,log出改变内容以及被观察者。

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSLog(@"%@---%@",change,object);
}

4.分别给两个变量赋值,用于触发KVO

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.p.name = @"name";
    self.p->age = 10;
}

.点语法与->的区别:
.点语法调用了setter方法,而->是直接访问成员变量

5.养成习惯,移除观察者

-(void)dealloc{
    [self.p removeObserver:self forKeyPath:@"name"];
    [self.p removeObserver:self forKeyPath:@"age"];
}

6.触发KVO后的发现log内容只有一个

2019-03-07 21:53:13.105997+0800 KVO[84542:11106254] {
    kind = 1;
    new = name;
}---<Person: 0x6000007a6600>

7.在@implementation中手动为agesetter方法

-(void)setAge:(int)newAge{
    age=newAge;
}

8.修改touchesBegan方法中的age赋值方法

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.p.name = @"name";
    [self.p setAge:10];
}

9.再次运行程序,触发KVO,观察log信息,会发现打印了两次。

2019-03-07 22:24:24.903100+0800 KVO[85477:11167107] {
    kind = 1;
    new = name;
}---<Person: 0x600002680a40>
2019-03-07 22:24:24.903477+0800 KVO[85477:11167107] {
    kind = 1;
    new = 10;
}---<Person: 0x600002680a40>

由此可见,只有对象实现了setter方法,KVO对其进行的观察,才会发起回调。

对象添加观察者后,发生了什么

  • 添加观察者后发生的变化

分别在添加观察者前后打印一下对象名称

self.p = [[Person alloc]init];
NSLog(@"%s",object_getClassName(self.p));
[self.p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
NSLog(@"------分割线----------");
NSLog(@"%s",object_getClassName(self.p));

打印结果为:

2019-03-08 10:13:13.099894+0800 KVO[88913:11408301] Person
2019-03-08 10:13:13.101816+0800 KVO[88913:11408301] ------分割线----------
2019-03-08 10:13:13.102021+0800 KVO[88913:11408301] NSKVONotifying_Person

从打印结果我们可以观察到,先前self.p指向的是Person,添加观察者后变成了NSKVONotifying_Person。由此可知,在我们为一个对象添加观察者之后,KVO会自动创建一个NSKVONotifying_<ClassName>

  • 新生成的类跟原来的类是什么关系?

这里有一个打印类跟子类的方法

-(void)printClasses:(Class)cls{
    //注册类的总量
    int count = objc_getClassList(NULL, 0);
    //创建一个数组,其中包含给定对象
    NSMutableArray *mArray = [NSMutableArray arrayWithObject:cls];
    //获取所有已经注册的类
    Class *classes = (Class *)malloc(sizeof(Class)*count);
    objc_getClassList(classes, count);
    for (int i = 0; i<count; i++) {
        //classes[i]的父类 等于 cls
        if (cls == class_getSuperclass(classes[i])) {
            [mArray addObject:classes[i]];
        }
    }
    //释放classes
    free(classes);
    NSLog(@"%@",mArray);
}

在添加观察者前,尝试打印一下[Person class]

[self printClasses:[Person class]];

打印结果为:

2019-03-07 22:52:02.545406+0800 KVO[86213:11217129](
    Person,
    Student
)

然后添加一个观察者,再重新打印一次

[self.p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
[self printClasses:[Person class]];

打印结果为:

2019-03-07 22:52:02.555585+0800 KVO[86213:11217129] (
    Person,
    "NSKVONotifying_Person",
    Student
)

所以KVO会自动创建的NSKVONotifying_<ClassName>类继承自<ClassName>

  • 新生成的类都做了什么?

这里有一个打印当前类执行的所有方法的方法

-(void)printClassAllMethod:(Class)cls{
    unsigned int  count = 0;
    //获取当前类对应的方法列表
    Method *methods = class_copyMethodList(cls, &count);
    for (int i = 0 ; i<count; i++) {
        Method method = methods[i];
        //分别获取SEL 跟 IMP
        SEL methodSEL = method_getName(method);
        IMP methodIMP = class_getMethodImplementation(cls, methodSEL);
        //打印SEL名称以及IMP地址
        NSLog(@"%@---%p",NSStringFromSelector(methodSEL),methodIMP);
    }
    //释放methods
    free(methods);
}

观察前后如果SEL数量发生改变,代表有对其进行了新添方法(为什么没删除?子类没办法操作父类方法)。如果IMP地址发生了改变,代表对这个IMP进行的重写。
验证:
新建一个Student类继承Person
Person中实现方法-(void)say;
Student重写-(void)say;并且实现方法-(void)study;
顺便重写一下原有方法 class方法
分别打印-(void)printClassAllMethod:(Class)cls

[self printClassAllMethod:[Person class]];
NSLog(@"------分割线----------");
[self printClassAllMethod:[Student class]];

打印结果:

2019-03-08 11:35:52.257273+0800 KVO[90824:11859981] say---0x109dfd260
2019-03-08 11:35:52.257500+0800 KVO[90824:11859981] ------分割线----------
2019-03-08 11:35:52.257659+0800 KVO[90824:11859981] say---0x109dfd1c0
2019-03-08 11:35:52.257774+0800 KVO[90824:11859981] study---0x109dfd1f0
2019-03-08 11:35:52.257976+0800 KVO[90824:11859981] class---0x109dfd220

其中,
say方法重写,IMP地址发生改变,并且打印出了新增的study方法以及重写的class方法。

分别打印[Person class][NSKVONotifying_Person class],对比原类跟新类都执行了什么方法。

[self printClassAllMethod:[Person class]];
[self.p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
NSLog(@"------分割线----------");
[self printClassAllMethod:NSClassFromString(@"NSKVONotifying_Person")];

打印结果:

2019-03-08 10:47:01.419689+0800 KVO[89658:11656677] .cxx_destruct---0x10b19c1b0
2019-03-08 10:47:01.419936+0800 KVO[89658:11656677] name---0x10b19c150
2019-03-08 10:47:01.420134+0800 KVO[89658:11656677] setName:---0x10b19c170
2019-03-08 10:47:01.421109+0800 KVO[89658:11656677] ------分割线----------
2019-03-08 10:47:01.421352+0800 KVO[89658:11656677] setName:---0x10b4f663a
2019-03-08 10:47:01.421516+0800 KVO[89658:11656677] class---0x10b4f506e
2019-03-08 10:47:01.421649+0800 KVO[89658:11656677] dealloc---0x10b4f4e12
2019-03-08 10:47:01.421782+0800 KVO[89658:11656677] _isKVOA---0x10b4f4e0a

所以,NSKVONotifying_Person重写了setterclassdealloc
添加了_isKVOA

总结

  • 只有对象实现了setter方法,KVO对其进行的观察,才会发起回调。
  • 对象添加观察者之后,KVO会自动创建一个NSKVONotifying_<ClassName>
  • 自动创建的NSKVONotifying_<ClassName>类继承自<ClassName>
  • NSKVONotifying_<ClassName>重写了setterclassdealloc,添加了_isKVOA

相关文章

  • IOS底层(三) KVO底层实现原理

    @[TOC](IOS底层(三) KVO底层实现原理 ) 一,KVO简述 KVO的全称 Key-Value Obse...

  • iOS底层原理探索—内存管理(一)

    探索底层原理,积累从点滴做起 往期回顾 iOS底层原理探索 — OC对象的本质 iOS底层原理探索 — class...

  • iOS - KVO

    [toc] 参考 KVO KVC 【 iOS--KVO的实现原理与具体应用 】 【 IOS-详解KVO底层实现 】...

  • iOS 底层探索:KVO 底层原理

    iOS 底层探索: 学习大纲 OC篇[/p/9d73ee7aae64] 前言 在Key-Value Observi...

  • iOS-底层原理21-KVO(下)

    iOS-底层原理21-KVO(下) 《iOS底层原理文章汇总》[https://www.jianshu.com/p...

  • iOS 探索KVO底层原理

    KVO底层原理 记 上一篇文章中说到,KVO监听成员变量无法收到回调。先验证一下是不是对的。 1.创建一个Pers...

  • iOS KVO底层原理探索

    一,KVO (Key-Value Observing) KVO是Objective-C对观察者设计模式的一种实现,...

  • 底层原理

    iOS底层原理总结 - Category的本质 KVO详解及底层实现青少年一定要读的KVO指南 iOS 底层解析w...

  • KVO学习笔记

    1.KVO初探学习2.KVO 底层原理探索 1.KVO初探学习 移除观察者的重要性 (IOS11之后说不移除是不对...

  • KVC

    KVC原理剖析 - CocoaChina_让移动开发更简单 iOS开发底层细究:KVC和KVO底层原理 | iOS...

网友评论

      本文标题:iOS 探索KVO底层原理

      本文链接:https://www.haomeiwen.com/subject/ljshpqtx.html