Runtime之objc_msgSend函数

首先了解一下什么是Runtime


Runtime顾名思义即为运行时。就是系统运行时候的一些机制,它提供了一些使得对象之间能够传递消息的重要函数,其中最主要的就是消息机制了。相较于C语言而言,C语言使用的是“静态绑定”,函数的调用在编译期就能知道运行期所需要调用的函数了,编译完成之后就按照顺序执行(面向过程就是这么任性)。


在对象上调用方法是OC 中经常使用的功能,用 OC 的术语来说,叫做传递消息.消息有名称和选择子,可以接受参数,而且可能有返回值.


OC 中,如果向某个对象发送消息,那么就会使用动态绑定机制来决定需要调用的方法,在底层所有方法都是c语言函数,然后对象在接收到消息之后,究竟该调用哪个方法则完全于运行期决定,甚至可以在运行时改变,说的具体点就是:使用[receiver message]语法即调用一个方法并不会马上执行receiver对象的message方法,而是向receiver发送一条message消息,这条消息可能由receiver来处理,也可能由其它对象来处理,也有可能假装没有收到这条消息,这些特性也使得oc成为了一门真正动态的语言.




objc_msgSend


给对象发送消息可以这样写:  


 id returnValue = [someObject messageName:parameter];

这个例子中, someObject叫做接受者,messageName叫做选择子,选择子与参数合起来叫做消息.编译器看到此消息之后,将其转换为一条标准的c语言函数,叫做objc_msgSend。官方函数如下


/* Basic Messaging Primitives
 *
 * On some architectures, use objc_msgSend_stret for some struct return types.
 * On some architectures, use objc_msgSend_fpret for some float return types.
 * On some architectures, use objc_msgSend_fp2ret for some float return types.
 *
 * These functions must be cast to an appropriate function pointer type 
 * before being called. 
 */
#if !OBJC_OLD_DISPATCH_PROTOTYPES
OBJC_EXPORT void
objc_msgSend(void /* id self, SEL op, ... */ )
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

从上面代码可以看到,这是最基本的用于发送消息的函数。注意:objc_msgSend并不能够发送所有类型的消息,只能发送基本消息。上面注释部分也说到,在一些处理器上,使用objc_msgSend_stret来发送返回值类型为结构体的消息,使用objc_msgSend_fpret或者objc_msgSend_fp2ret来发送返回值为浮点类型的消息。

objc_msgSend其原型为

id objc_msgSend(id self, SEL op, ...)

理解一下几个函数:


objc_msgSend_stret():如果待发送的消息要返回结构体,那么交给此函数处理.只有当CPU的寄存器能够容纳得下消息返回类型时,这个函数才能够处理此消息.若是无法容纳于CPU 寄存器中,那么就由另一个函数执行派发.此时,那个函数会通过分配在栈上的某个变量来处理消息所返回的结构体.


objc_msgSend_fpret():如果消息返回的是浮点数,那么可由此函数来处理,在某些架构的 CPU 中调用函数时,需要对"浮点数寄存器"做特殊处理.


objc_msgSendSuper():如果要对超类发送消息,那么就要交给此函数处理.也有与objc_msgSend_stret(),objc_msgSend_fpret()两个等效的函数用于处理超类的相应消息.


参数解释

这是一个参数个数可变的函数,能够接收两个或者两个以上的参数.


第一个参数代表接受者,objc_msgSend第一个参数类型id,它是一个指向类实例的指针.typedef struct objc_object *idobjc_object原型为:

 

 struct objc_object { Class isa ; };

 objc_object结构体包含一个isa指针,根据isa指针就可以找到对象所属的类.


第二个参数代表选择子(SEL是选择子的类型),它是selectorObjc中的表示类型(Swift中是Selector类)。selector是方法选择器,可以理解为区分方法的 ID,而这个 ID 的数据结构是SEL:

   

  typedef struct objc_selector *SEL;

其实它就是个映射到方法的C字符串,你可以用 Objc 编译器命令@selector()或者 Runtime 系统的sel_registerName函数来获得一个SEL类型的方法选择器。

   

后续参数就是消息中的那些参数,其顺序不变,选择子指的就是方法的名称.


编译器会把刚才那个例子中的消息装换成为如下的函数 :


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

 objc_msgSend函数会依据接受者与选择子的类型来调用适当的方法.为了完成此操作,该方法需要在接受者所属的类中搜索其"方法列表",如果能够找到与选择子名称相符合的方法,就跳转至其实现代码.若是找不到,那就沿着继承体系继续向上査找,等找到了合适的方法之后再调整.如果最终还是找不到相符合的方法,那么就执行"消息转发".说来,想调用一个方法似乎需要很多步骤.但是幸运的是,objc_msgSend会将匹配的结果缓存到"快速映射表"里面,每个类都有这样一块缓存,若是后面还向该类发送与选择子相同的消息,那么执行起来就很快了.实际一点的例子:



当你写下面这样的代码时

[tableView cellForRowAtIndexPath:indexPath];

编译器实际上把它转换成下面这样的C函数调用

objc_msgSend(tableView, @selector(cellForRowAtIndexPath:), indexPath);


使用objc_msgSend函数

创建MsgSendClass类

在oc中我们初始化一个对象会调用alloc和init方法

 MsgSendClass *msgObject = [[MsgSendClass alloc]init];

在经过编译之后,会转换为如下代码

 // 创建对象
 MsgSendClass *msgObject = ((MsgSendClass * (*) (id, SEL)) objc_msgSend)((id) [MsgSendClass class], @selector(alloc));
 // 初始化对象
 msgObject = ((MsgSendClass * (*) (id, SEL)) objc_msgSend)((id) msgObject, @selector(init));

MsgSendClass * 表示返回类型

(*)表示函数指针

(id, SEL)是参数列表,一般id是类本身,SEL是需要执行方法的选择子,参数可以传多个,将创建对象和初始化对象合并

MsgSendClass *msgObject = ((MsgSendClass * (*) (id, SEL)) objc_msgSend)((id) [MsgSendClass class], @selector(alloc), @selector(init));

使用一下

我们为MsgSendClass添加几个方法,分别是无参数无返回值、带单个参数无返回值、带参数和返回值等

#import "MsgSendClass.h"

@implementation MsgSendClass

// 无参数无返回值
-(void)testNoArgumentAndNoRetureVlaue {
    NSLog(@"%s was called", __func__);
}

// 带单个参数无返回值
-(void)testHasArgument:(NSString *)arg {
    NSLog(@"%s was called: %@", __func__, arg);
}

// 带参数和返回值
-(BOOL)testHasArguemntAndRetureValue:(NSString *)arg {
    NSLog(@"%s was called : %@", __func__, arg);
    if ([arg isEqualToString:@"MsgSendClass"]) {
        return  true;
    }
    return false;
}

@end

无参数无返回值

((void *(*) (id, SEL))objc_msgSend)((id)msgObject, @selector(testNoArgumentAndNoRetureVlaue));

使用objc_msgSend方法执行msgObject对象的testNoArgumentAndNoReturnValue方法,可以看到打印如下

ObjcMessage[65257:6115921] -[MsgSendClass testNoArgumentAndNoRetureVlaue] was called

带单个参数无返回值

 ((void *(*) (id, SEL, NSString *))objc_msgSend)((id)msgObject, @selector(testHasArgument:), @"带一个参数,但无返回值");

同上操作,打印如下

ObjcMessage[65357:6129908] -[MsgSendClass testHasArgument:] was called: 带一个参数,但无返回值

带参数和返回值

BOOL value = ((BOOL *(*) (id, SEL, NSString *))objc_msgSend)((id)msgObject, @selector(testHasArgument:), @"MsgSendClass");

同上操作,打印如下

ObjcMessage[65357:6129908] -[MsgSendClass testHasArgument:] was called: MsgSendClass

参考

runtime object_msgSend



猜你喜欢

转载自blog.csdn.net/longshihua/article/details/79751927