每个 Objective-C 对象都会指向一个类,而每个类又包含一个方法列表。每个方法则由选择器(selector)、函数指针和一些元数据(metadata)构成。objc_msgSend 职责就是接收对象(object)和选择器(selector),根据选择器名称找到对应方法的函数指针并跳转执行该函数。
查找过程相对来说还是比较复杂的。若某个方法在当前类中未找到,就需要沿着继承链继续在父类中查找。如果在父类中也未查询到的话,则会触发 runtime 机制中的消息转发机制。任何对象在接收到第一条消息后都会触发类方法 +initialize 。
为了解决这对矛盾提高查询速度,Objective-C 采用了方法缓存策略。每个类都会使用哈希表将其方法按照 Selector - IMPs(函数指针) 键值对关系缓存起来。这样在查询方法时,runtime 首先会直接去哈希表中查询。如果哈希表中不存在的话则转而执行原有复杂、缓慢的处理流程,并将最终结果缓存起来已备下次使用。
objc_msgSend 用汇编语言进行实现,具体理由有两个:首先纯 C 语言无法实现这么一个函数:接收不定个数且未知类型的参数作为入参跳转至任意函数指针(即调用实现);其次,执行速度对 objc_msgSend 来说非常重要,汇编语言能最大化提升该项指标。
当然,使用汇编语言实现整个复杂的消息处理过程是不现实的,而且也没这种必要。因为有些流程一旦触发程序都会变慢,无论采用何种语言层面的实现。整个消息处理流程代码可以分为两个部分:通过汇编代码实现的快速路径部分(fast path) ,C 语言实现的慢路径流程(slow path)。其中汇编代码对应缓存表中查询方法部分并且未命中时跳转 C 代码来进行下一步处理。
获取消息对象所对应的类信息
获取类所对应的方法缓存
在方法缓存中查询 selector 对象的函数实现
如果查询失败则调用 C 代码进行下一步处理
跳转到 IMP 所指的函数实现