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方法没有实现 动态运行时语言将函数决议推迟到运行时 编译时语言在编译起进行函数决议
能否向编译后的类添加实例变量
不行
能否向动态添加的类添加实例变量
可以