热门搜索 :
考研考公
您的当前位置:首页正文

发送消息,理解 objc_msgSend 的作用

来源:东饰资讯网

消息传递

OC 语言中,对象调用方法我们称之为发送消息,或者叫做“传递消息”(pass a message)。消息具有“名称”(name)或“选择子”(selector),实际上就是方法名称,可以接受参数,也可以有返回值。在 OC 中我们是这样发送消息的,结构如下:

id retrunValue = [someObject messageName:parameter];

其中returnValue是返回值;someObject是接受者,即调用这个方法的对象;messageName:是选择子,即方法名称;parameter是参数,即调用方法的时候传入的参数。选择子(selector)与参数(parameter)合起来就是我们称为的“消息”。在程序编译期的时候,所有类似这样的消息最终都会被转换成一条标准的C语言函数内供程序调用,其结构如下:

id objc_msgSend ( id self, SEL cmd, ... );

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

讲上面的那个OC的例子转换后的原型如下:

id returnValue = objc_msgSend(someObject, @selector(messageName:),parameter);

在程序运行的时候objc_msgSend函数会根据接受者和选择子在相对应的类中查找方法,在对应类中有一个“方法列表”如果能找到对应的方法就执行此方法,若不能就沿着继承体向上父类继续查找,如果最终还是找不到相对应的方法就会执行“消息转发(message forwarding)”。

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

objc_object, objc_class 以及 Ojbc_method

OC中,类,对象和方法其实都是一个C的结构体,这点我忙在objc/objc.hruntime.h的文件中就能找到他们的定义。

#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 isa  OBJC_ISA_AVAILABILITY;

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

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等相关的信息。

总结

那么总结来说当在代码中写下一个 OC 语言发送的消息后,那么这条 OC 语言的消息首先经过编译器,就会被编译器转换成这样类似结构的一条 C 语言函数:

id objc_msgSend ( id self, SEL cmd, ... );

举个例子🌰:

objc_msgSend(dog,@selector(eat:),)

1.首先这个函数就会根据传入的dog这个类的 isa 指针找到他的 classobjc_class

2.在 class 中找到这个类的所有的相关信息

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

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

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

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

7.一旦找到这个方法,就去执行它的实现 IMP

但是如果最终一直都没有找到 eat 这个相对应的方法呢?通常程序就会crash并抛出异常,但是在抛出异常前首先会执行消息转发,关于消息转发的相关学习和笔记我将会在下一篇的博客中整理出来。

Top