美文网首页iOS学习笔记
runtime 笔记(方法调用-消息传递过程)

runtime 笔记(方法调用-消息传递过程)

作者: ToBeABetter_man | 来源:发表于2019-08-09 09:52 被阅读0次

什么是runtime?

Objective-C 是一门动态语言,相比C语言,是在其基础上增加了面向对象的特性和消息传递机制。而面向对象的特性和消息传递机制实现的关键就是runtime,也就是常说的运行时机制。

当我们平时使用OC去调用一个方法时,其实是在运行时通过runtime给对象发消息。那么这里的发消息和我们C语言里去调用一个函数有什么区别呢?在C语言里我们去调用一个方法(函数),其实是就是去调用内存中的一段代码,这里的函数名其实是和内存中的代码段绑定在一起的,而且这个绑定过程在编译阶段就已经确定下来了。而在OC中我们去调用一个对象的方法,并不会立即去执行这个方法,而是会先向这个对象发送一个消息,而具体调用的代码在这个时候是不确定的,因为最后调用的方法可能会是这个对象的其它的方法,也有可能是其他对象的方法。在这期间决定最后调用的方法的过程就是消息传递的过程。

runtime的消息传递过程

在了解消息传递过程之前,我们要先知道接收消息的对象本质是什么,我们看一下objc.h中objc_object的定义

#if !OBJC_TYPES_DEFINED
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;
#endif

看上面的代码我们知道我们创建的对象其实是一个struct objc_object 结构体,我们平常使用的id其实也是这个结构体,这个结构体中只有一个变量,一个Class类型的变量,这个变量的是一个struct objc_class结构体。

在消息传递的过程中,runtime会先通过object的isa指针来找到这个对象所属的类,也就是上面的struct objc_class,然后在这个类对象的方法列表中去寻找。这里我们先了解一下struct objc_class里有哪些东西:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

上面代码中我们可以看到有cache和methodLists两个参数,而查找方法也是在这两个变量中去找的。为了优化性能和缩短方法的查找时间,objc_class的cache结构体中存储的是每一次类或者实例对象调用的方法。

1.当类或者实例对象调用方法时,会优先在cache中寻找相应的方法,如果找到了,则直接去找method的IMP实现并执行。如果找不到那就methodLists中去遍历查找,如果在methodLists找到了则会执行该方法IMP,并且把method的method_name作为key,method的IMP作为value保存到objc_cache中。

PS:这里说一下IMP是什么,IMP函数指针指向了方法实现的首地址,当 Objective-C发起消息时,最终执行的方法是由IMP决定的。

2.如果在methodLists中没有找到方法,则会去根据super_class获取到当前类的父类,重复1中的查找(如果是类方法则去找该类的元类)。如果找到了则返回,否则继续向上查找。

3.如果找到NSObject类还没有找到,则会进入消息转发阶段。

消息转发:

1.动态方法解析

动态解析,当没有找到方法时,Runtime为我们提供了一次动态添加方法实现的机会,让你去提供一个函数的实现。如果你实现了这两个方法并添加了新的方法,那么runtime会重新启动一次消息发送过程。

具体实现主要通过下面的三个方法

// 实现这个方法来添加新的类方法
+ (BOOL)resolveClassMethod:(SEL)sel;

// 实现这个方法来添加新的对象方法
+ (BOOL)resolveInstanceMethod:(SEL)sel;

/**
 运行时方法:向指定类中添加特定方法实现的操作
 @param cls 被添加方法的所属的类
 @param name 被添加方法的selector方法名
 @param imp 被添加方法的实现
 @param types 被添加方法的返回值与参数类型
 @return 添加方法成功还是失败
 */
BOOL class_addMethod(Class _Nullable cls,
                     SEL _Nonnull name,
                     IMP _Nonnull imp,
                     const char * _Nullable types)
SEL:SEL是表示一个方法的selector指针,OC会根据不同方法的名字和,参数序列,生成唯一的标识,也就是这个指针的地址,所以就不难理解为什么OC中不能定义方法名相同但参数不同的方法了。

2.消息接受者重定向

-(id)forwardingTargetForSelector:(SEL)aSelector;

如果你没有在动态方法解析时添加方法,那接下来runtime会检查你是否实现了(id)forwardingTargetForSelector:(SEL)aSelector方法,如果实现了该方法则会将消息转发给你返回的对象,重复上面的消息查找过程。

3.消息重定向

// 通过方法调用者创建方法签名,然后将这个签名通过NSInvocation封装成方法调用传给重定向方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
// 拿到传过来的invocation参数,再次进行IMP查找
- (void)forwardInvocation:(NSInvocation *)anInvocation;

如果前面的两次机会都没有挽回,这时runtime会启动完整的消息转发机制

1.先发送- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector消息,拿到方法的参数和返回值信息,如果实现了该方法,并返回一个方法签名,runtime就会创建一个 NSInvocation对象并发送给- (void)forwardInvocation:(NSInvocation *)anInvocation方法通知该对象,给予此次消息发送的最后一次寻找IMP的机会。
2.如果没有实现该方法或返回值为nil,那么runtime则会发出doesNotRecognizeSelector消息,控制台输出unrecognized selector sent to instance同时直接Crash掉。

至此整个消息传递过程就结束了,这里总结一下:
1.我们创建的对象是一个objc_object结构体
2.调用方法时其实是通过runtime给对象发消息
3.消息的传递过程是根据结构体的Class参数去寻找所属类,并去cache和methodLists里去查找。
3.如果没找到则开始进行 消息转发机制(消息动态解析,消息接受者重定向,消息重定向)

相关文章

  • runtime 笔记(方法调用-消息传递过程)

    什么是runtime? Objective-C 是一门动态语言,相比C语言,是在其基础上增加了面向对象的特性和消息...

  • runtime的实用性讲解

    runtime 概念 runtime 运行时机制,主要是消息机制。OC 中的方法调用属于消息的发送,动态调用过程,...

  • iOS Runtime运用之一 消息传递objc_msgSend

    关于runtime的运用有:1 消息传递(调用方法): objc_msgSend2 动态添加方法 : class_...

  • iOS - Runtime - 概念和方法交换

    runtime的概述runtime的相关概念runtime消息机制消息传递动态方法解析消息转发runtime的作用...

  • 方法调用底层实现

    runtime怎么实现方法的调用 :消息机制,runtime系统会把方法调用转化为消息发送。即objc-msgSe...

  • Runtime系列之消息转发

    Runtime系列之消息转发 1 消息传递 OC属于动态类型语言,方法的调用是通过发送消息来完成的。objc/ru...

  • Runtime消息传递总结

    iOS中类找不到方法时消息处理机制 iOS~runtime理解 一、正常的消息传递 在C等语言中,调用一个方法就是...

  • Runtime 的应用

    前面我们说到:Runtime 消息传递机制Runtime 消息转发机制Runtime 交换方法今天我们来谈谈Run...

  • Objective-C的方法交换

    Objective-C 方法调用实际就是一个消息传递的过程。在消息传递过程中,根据消息的选择器SEL找到IMP执行...

  • iOS面试-Runtime简介

    本文主要介绍runtime的五点 Runtime简介 Runtime(消息机制) Runtime方法调用流程 Ru...

网友评论

    本文标题:runtime 笔记(方法调用-消息传递过程)

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