消息机制和runtime的相关应用

2021-03-11  本文已影响0人  牛奶红茶

1.objc_msgSend执行流程

-OC中的方法调用其实就是转成objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)

-objc_msgSend的执行流程可以分为3大阶段

->1.消息发送

消息发送流程

消息发送给方法查找的流程

对象方法流程

对象的实例方法-自己有-返回自己

对象的实例方法-自己没有-父类有-返回父类的

对象的实例方法-自己没有-父类也没有-找父类的父类-NSObject有-返回NSObject的

对象的实例方法-自己没有-父类也没有-找父类的父类-NSObject也没有-崩溃

类方法流程

类方法-自己有-返回自己

类方法-自己没有-父类有-返回父类的

类方法-自己没有-父类也没有-找父类的父类-NSObject有-返回NSObject的

类方法-自己没有-父类也没有-找父类的父类-NSObject也没有--但是有对象方法-返回对象方法

类方法-自己没有-父类也没有-找父类的父类-NSObject也没有--对象方法也没有-崩溃

而类方法找到最后没有找到但是NSObject的对象方法有重名则执行对象方法,这条则是下图中(meta)Root class指向(class)Root class的体现

类的结构体系

->2.动态方法解析

动态方法解析(动态添加方法实现)

是否曾经有动态解析->曾经有动态解析->直接走消息发送

是否曾经有动态解析->未有动态解析->调用+ (BOOL)resolveClassMethod:(SEL)sel,或+ (BOOL)resolveInstanceMethod:(SEL)sel来动态解析方法,->标记威已经动态解析->走消息发送(动态解析后,会重新走消息发送流程,即"从receiverClass的cache中查找方法"这一步开始执行)

->3.消息转发

消息转发的流程(以上方法都有对象方法和类方法两个版本,类方法只需要在前面加+即可)

- (id)forwardingTargetForSelector:(SEL)aSelector 如果返回一个类,则会在该类中处理aSelector方法

- (void)forwardInvocation:(NSInvocation *)anInvocation 一旦执行到该方法中,则可以在该方法里自定义任何逻辑,消息到此也就结束了,并不会导致崩溃

2.Runtime相关

->1.什么是Runtime?平时项目中使用过吗?

--OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行

--OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数,平时编写的 OC代码,底层个都是转成Runtime API进行调用

->2.Runtime的具体应用

--利用关联对象(AssociatedObject)给分类添加属性

--遍历类的所有成员变量(秀尴尬癌textfield的占位文字颜色,字典转模型,自动归档解档)

--交换方法实现(交换系统的方法,可以防止数组越界,字典设置空崩溃等)

--利用消息转发机制解决方法找不到的异常问题

3. 能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

不能向编译后得到的类中增加实例变量,因为编译后的类已经注册到runtime中,类的结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定,同时runtime会调用class_setIvarLayout或class_setWeakIvarLayout来处理strong weak 引用,所哟不能向存在的类中添加实例变量

可以向运行时创建的类中添加实例变量,调用class_addIvar函数,但是必须要在调用objc_allocateClassPair之后,在objc_registerClassPair之前。其实就是在编译过程中添加实例变量,编译后类的实例链表,方法链表,变量大小都已经确定了

4.SEL和Method,IMP

objc_method

objc_method结构体中的内容

SEL method_name 方法名

char *method_types方法类型

IMP method_imp 方法实现

SEL

SEL  typedef struct objc_selector *SEL 代表方法的名称,翻译成选择子或选择器,代表方法在Runtime期间的标识符,objc_selector结构体本质上上时一个个C字符串。在类加载的时候,编译器会生成与方法相对应的选择子,并注册大奥objective-C的Runtime运行系统,不论两个类是否存在依存关系,只要他们拥有相同的方法名,返回值等,那么他们的SEL就是相同的

SEL sel = @selector(methodName)

IMP

IMP :typedef id (*IMP)(id, SEL, ...),代表函数指针,即函数执行的入口,该函数使用标准的C调用,第一个参数支向self(它代表当前类实例的地址,如果是类则指向的是它的元类),作为消息的接受者,第二个参数代表选择子(选择器),......代表可选参数,前面的id代表返回值

Method

Method typedef struct objc_method *Method Method对开发者来说是一种不透明的类型,被隐藏在我们平时书写的类或对象的方法背后,他是一个objc_method结构体指针,该结构体中包含一个SEL和IMP,实际上相当于在SEL和IMP做了一个映射,有了SEL我们可以找到IMP,从而调用方法的实现代码

方法名method_name类型为SEL,前面提到过相同名字的方法即使在不同类中定义,它们的方法选择器也相。方法类型method_types是个char指针,其实存储着方法的参数类型和返回值类型,即是Type Encoding编码。method_imp指向方法的实现,本质上是一个函数的指针

5.runtime 如何通过selector找到对应的IMP地址?

实例方法:每个实例的isa指针指向着对应类对象,而每一个类对象中都有一个对象方法列表

类方法:每个类对象的isa指针都指向着对应的元类对象,而每一个元类对象中都有一个类方法列表

->当我们发送一个消息给一个NSObject对象时,这条消息会在对象的类对象方法列表里查找

->当我们发送一个消息给一个类时,这条消息会在类的meta class对象的方法别表里查找

在寻找IMP的地址时,runtime提供管理两种方法

1.IMP class_getMethodImplementation(Class cls, SEL name);

2.IMP method_getImplementation(Method m)

第一中方法,类方法和实例方法实际上都是通过调用class_getMethodImplementation()来寻找IMP地址的,不同之处在于传入的第一个参数不同,通过传入的参数不同,找到不同的方法劣币啊嗷,方法列表中保存着方法信息的结构体(objc_method),结构体中包含方法的实现,selector本质就是方法的名称,通过该方法名称,即可在结构体中找到相应的实现

类方法(假设有一个类A)

class_getMethodImplementation(objc_getMetaClass("A"),@selector(methodName));

实例方法

class_getMethodImplementation([A class],@selector(methodName));

第二种方法,传入的参数只有method,区分类方法和实例方法在于封装method的函数

类方法

Method class_getClassMethod(Class cls, SEL name)

实例方法

Method class_getInstanceMethod(Class cls, SEL name)

最后调用 IMP method_getImplementation(Method m)获取IMP地址

---

---

---

===

上一篇 下一篇

猜你喜欢

热点阅读