iOS Runtime的知识整理

·  阅读 153

1.数据结构

1.objc_object id = objc_object

对象:OC中的对象其实是一个objc_object的指针类型,他的结构体里面就一个isa指针,指向对应的类。

2.类对象与元类对象

Class = objc_class,他继承自objc_object.

superClass 父类

cache 方法的缓存

bits 里面保存着变量,属性,方法,协议

OC的Class类型包括如下数据(即:元数据metadata):super_class(父类类对象);name(类对象的名称);version、info(版本和相关信息);instance_size(实例内存大小);ivars(实例变量列表);methodLists(方法列表);cache(缓存);protocols(实现的协议列表);

Class里面保存着实例方法列表,属性列表,成员变量列表等相关信息

类对象的isa指针,指向元类,元类里面保存着类方法的信息

元类对象的isa指针指向根元类,根元类的isa指针指向他本身,根元类的superclass指向根类

为什么要设计元类
1.单一职责原则
2.类对象,元类对象能复用消息发送流程机制

class_copyIvarList & class_copyPropertyList区别
class_copyIvarList获取的是class_ro_t的属性和成员变量列表,类扩展的属性和成员变量也都在内
class_copyPropertyList获取的事当前类的属性列表
复制代码

cache_t 用户快速查找方法执行函数,他是一个可增量扩展的hash表结构

key对应的是方法选择器,也就是selector。imp是一个无类型的函数指针.就是一个函数体 当给我们一个key,我们可以通过hash查找算法,来找到对应的bucket_t这个数据结构。然后定位到具体的方法实现

class_rw_t

class_ro_t

method_t

runtime的数据结构

3.消息传递

objc_msgSend(id, sel,...)

objc_msgSendSuper(id, sel,...)

[self class] , [super class]打印结果 因为方法的接收者都是self,所以打印的结果都是self

首先会在缓存中查找,缓存中如果没有的话,就去当前类方法中查找,如果还没有就逐级去父类查找,如果父类也还没有的话就开始消息的转发流程

缓存查找 通过给定的方法选择器通过hash查找找到对应的bucket_t,然后拿到对应的函数实现,也就是imp

在类中查找方法 对于已经排好序的方法列表,通过二分查找算法查找对应的执行函数

对于没有排序的方法列表,通过普通遍历的方式查找对应的执行函数

4.消息转发

第一阶段:咨询接收者,询问它是否可以动态增加这个方法实现

第二阶段:在第一阶段中,接收者无法动态增加这个方法实现,那么系统将询问是否有其他对象可能执行该方法,如果可以,系统将转发给这个对象处理。

第三阶段:在第二阶段中,如果没有其他对象可以处理,那么系统将该消息相关的细节封装成NSInvocation对象,再给接收者最后一次机会,如果这里仍然无法处理,接收者将收到doesNotRecognizeSelector方法调用,此时程序将crash。

5.方法的交换

方法交换的注意点:
1.方法的交换应该放在load方法里面
2.为了避免Swizzling的代码被重复执行,我们可以通过dispatch_once函数来执行
3.Swizzling在load中执行时,不要调用[super load],如果多次调用了[super load],可能会出现交换无效的错误

方法交换的实际运用
1.比如我们要统计某个界面的浏览次数,我们可以自定义一个方法用来做统计业务,然后在load方法里面,我们和系统的方法交换。

动态添加方法

performSelector

performSelecotr主要用来调用动态添加的方法的。 他有一个注意点:如果使用的那个有延迟的方法,要确保当前线程的runloop开启了才能执行

动态方法解析

@dynamic修饰的属性是在运行时添加的,不是在编译时添加,他的set,get方法没有实现 动态运行时语言将函数决议推迟到运行时 编译时语言在编译起进行函数决议

能否向编译后的类添加实例变量

不行

能否向动态添加的类添加实例变量

可以


分类:
iOS
标签:
分类:
iOS
标签:
收藏成功!
已添加到「」, 点击更改