1.objc_msgSend函数
在Objective-C(以下简称OC)中,消息只有在运行期间才会绑定到具体的方法实现。编译器会将一个发送消息转为一个C函数,例如[receiver message]
会转为objc_msgSend。此函数将receiver和message作为两个主要的参数。也即objc_msgSend(receiver, message)
。此函数则可以在运行期进行动态绑定。主要过程包括一下三个:
①首先找到message指向的方法实现。由于同一个方法名称可能在不同的类中有实现,objc_msgSend通过receiver找到正确的方法;
②然后调用方法,将方法及其参数发送给receiver;
③最后,将方法的返回值作为自己的返回值。
根据苹果官方文档注明的,我们不能直接调用此方法。
消息转发是建立在编译器为每个类和对象所建立的结构基础上的,每个类包括了两个基本的元素(elements):
①一个指向父类的指针。
②一个类转发表(dispatch table)。这个表是消息选择器(selector)及与之相关联的消息地址的入口。
下图是类和对象架构的原理图
当一个新的对象被创建,并分配内存,它的实例变量被初始化。第一个实例变量是一个名叫isa的指针,它指向自己的类结构(class structure)。它使对象能够操作(access)自己的类以及父类。
当一个消息被发送时,消息发送函数通过isa指针去找到类,并在这个类的转发表中去寻找这个方法,如果在此类中没有找到此方法,objc_msgSend则会顺着指针往父类的转发表中查询,如果父类依然找不到,则会顺着继承树往上继续查找,一直查找到NSOjbect类。
2.获取消息地址
如果不想在运行期进行动态绑定,则可以获取消息地址进行直接调用。可以通过方法methodForSelector:来获取指向消息的指针。以下是代码示例:
//声明一个指向函数的指针setter
void (*setter)(id, SEL, BOOL);
int i;
//通过方法methodForSelector获取selector的函数地址
setter = (void (*)(id, SEL, BOOL))[target methodForSelector:@selector(method)];
//循环调用method 100次
for (i = 0; i < 100; i++) {
setter(targetList[i], @selector(method), YES);
}
3.给类动态添加方法
我们还可以给类动态添加方法,代码如下
+ (BOOL)addMethods:(SEL)aSEL {
class_addMethod([self class], aSEL, (IMP)dynamicMethodIMP, @"v@:");
}
class_addMethod第一个参数指定方法添加到哪个类, aSEL为方法名称,
IMP是implementation的简称,指向具体实现的地址,他必须接受两个参数:self 和 _cmd。例如:
void dynamicMethodIMP(id self, SEL _cmd) {
}