片头
像[rev message],就是向对象rev发送一条为叫message的消息。
OC的世界是很神奇的,不到程序运行,都不知道它最终会有执行什么东西。
为什么这么说?
原理
消息发送是基于NSObject类的一套动态函数调用机制,由runtime库提供实现。
编译器会把你写的代码转换为相应的C静态函数。
[rev message]的message,会被编译成一条C函数message=>c_message。对象rev调用message方法,就是去找message的C函数。
举个例子:
类Person有一条say:方法
@implememtation Person
- (void)say:(NSString *)something{
// todo..
NSLog(@"%@", something);
}
向Person类实例man发送say消息
Person *man= [Person new];
[man say:@"hello"];// - 控制台打印hello
编译器处理,
- -say:方法被编译成C静态函数:
static void _I_Person_run(Person *self,SEL _cmd,NSString *something){
// todo..
// NSLog((NSString *)__NSConstantStringImpl__var_folders_gm_0jk35cwn1d3326x0061qym280000gn_T_main_a5cecc_mi_0, @"hello")
prinf("something");// 伪代码
}
- [man say:@"hello"]:
objc_msgSend(man,@selector(say:),@"hello")
其中objc_msgSend是在runtime库定义的一条C系统函数,原型为 id objc_msgSend(id, SEL , ..)。
也就是说向man发送say:消息,会被编译成objc_msgSend函数调用,并向其依次传递参数(id)man,(SEL)@selector(say:) 和 (NSString *)@"hello"。
在程序到运行时,
- 建立全局映射关系,用伪代码表示的话:
{
"Person类":{
@selector(say:) : &_c_say,// say编译出来的C静态函数
@selector(run:) : &_c_run,// 假设还有run方法
..
}
"其它类":..
..
}
- 当objc_msgSend被执行时,其内部通过传递的参数@selector(say:),去找到say:方法被编译成的C静态函数的地址(IMP指针)
IMP ptr = class_getMethodImplementation([Person class], @selector(say:));
- 并以函数指针方式完成调用,即调用的是_c_say。
ptr(man,@selector(say:),@hello);
补充
如果手动去干预映射关系,让:
// 交换映射位置
{
@selector(say:) : &_c_run,
@selector(run:) : &_c_say,
..
}
那么,当程序运行到[man say:@hello]时,其实际执行的却是@selector(say:)对应的-run:方法的C函数。即在表现上明明是调用了-say:方法,却发现是-run:方法被调用了。老板,你挂羊头卖狗肉,货不对版!
不,请叫我“黑魔法”方法交换(method swizzle)。通俗原理就是这样了。
参考
Objective-C Runtime Programming Guide
网友评论