首先了解一下什么是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 *id; objc_object原型为:
struct objc_object { Class isa ; };
objc_object结构体包含一个isa指针,根据isa指针就可以找到对象所属的类.
第二个参数代表选择子(SEL是选择子的类型),它是selector在Objc中的表示类型(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
参考