美文网首页编写高质量代码的52个有效方法
52个有效方法(11) - 理解objc_msgSend的作用

52个有效方法(11) - 理解objc_msgSend的作用

作者: SkyMing一C | 来源:发表于2018-08-30 17:19 被阅读12次
消息机制

OC 语言中,方法调用实际上就是消息发送。

objc_msgSend(id obj, SEL cmd,...)

这是一个参数个数可变的函数,能够接收多个的参数,其中第一个参数代表接收者,第二个参数代码选择子(SEL是选择子的类型),后面其他的消息就是发送消息中所传入的参数。

消息具有“名称”(name)或“选择子”(selector),实际上就是方法名称,可以接受参数,也可以有返回值。

发送消息的方式

在 OC 中我们是这样发送消息的:

id returnValue = [someObject messageName:parameter];

someObject叫做“接受者”(receiver),messageName叫做“选择子”(selector)。选择子与参数合起来称为“消息”(message).编译器看到此消息后,将其转换为一条标准的C语言函数调用,所调用的函数乃是消息传递机制中的核心函数,就是objc_msgSend,编译器把上面的方法调用会转换为如下函数。

id returnValue = objc_msgSend(someObject,@selector(messageName:),parameter);
方法调用的过程
  • objc_msgSend函数会依据接受者与选择子的类型来调用适当的方法。

  • 为了完成此操作,该方法需要在接受者所属的类中搜寻其“方法列表”(list of methods)。

  • 如果能找到与选择子名称相符的方法,就跳至其实现代码;若是找不到,就沿着继承体系继续向上查找,等找到合适的方法之后再跳转。

  • 如果最终还是找不到相符的方法,那就执行“消息转发”(mesageforwarding)操作。

快速映射表

​在调用方法的时候执行的步骤很多,但是objc_msgSend会将匹配到的方法列表中的方法缓存起来,其缓存在一个叫“快速映射表”里面。每一个类都有这样的一块缓存,在该类下次再执行相同的消息的时候就会优先从“快速映射表”中查找,而选择子就是查找方法时所用到的键。

方法调用的优先级

分类(最后参与编译的分类优先) --> 原来类 --> 父类

在OC中,类,对象和方法其实都是一个C的结构体
#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 isa  OBJC_ISA_AVAILABILITY;
};

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

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;

typedef struct objc_method *Method;
  • objc_object实际上是一个指向 Class 结构体类型的指针,指向对象的类,而 Class 中也有一个 isa 指针,指向了元类,元类中则存储了该类的方法列表objc_method_list

  • objc_method_list本质是一个有 objc_method 元素的可变长度的数组。一个 objc_method 结构体中有函数名,也就是SEL,有表示函数类型的字符串,以及函数的实现IMP。

  • objc_class就是前面说到的被objc_object中那个 isa 指针所指向的 Class 的类了。这个objc_class其实是一个结构体,其中在这个结构体中就包含了很多这个类的信息。其中就包括了上文中提到的储存这个类中当前方法的链表objc_method_list和储存这个类中被执行过的方法的缓存objc_cache等相关的信息。

方法调用的具体实例
objc_msgSend(dog,@selector(eat:),meat);
  • 首先这个函数就会根据传入的dog这个类的 isa 指针找到他的 class即objc_class。

  • 在 class 中找到这个类的所有的相关信息。

  • 然后会首先的从 class 中的objc_cache去查找eat 方法。

  • 如果在objc_cache中没有找到 eat 这个方法,则说明 eat 方法没有被执行过,在缓存中没有。

  • 然后进一步到这个类中的objc_method_list中找和选择器中对应的方法eat。

  • 如果在 class 中没有找到相关的方法,则会继续向上查找,在父类(super_class)中去找相对应的方法。

  • 一旦找到这个方法,就去执行它的实现 IMP(一个函数指针,保存了方法的地址)。

  • 但是如果最终一直都没有找到 eat 这个相对应的方法呢?通常程序就会crash并抛出异常,但是在抛出异常前首先会执行消息转发。

要点
  1. 消息由接受者、选择子及参数构成。给某对象“发送消息”(invoke a message)也就相当于在该对象上“调用方法”(call a method)。

  2. 发给对象的全部消息都要由“动态消息派发系统”来处理,该系统会查出对应的方法,并执行其代码。

相关文章

网友评论

    本文标题:52个有效方法(11) - 理解objc_msgSend的作用

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