objc_msgSend的执行流程可以分为三大阶段:消息发送、动态方法解析、消息转发
1.消息发送
大致流程:先在本类的方法缓存列表中查找、再在本类的方法列表中查找(如果是类方法在元类的方法列表中查找)、如果还没找到继续在父类中查找。详细流程看下图:
0.消息发送.png
注:方法只会缓存在receiverClass的cache中,而不会在父类中缓存。
2.动态方法解析
如果第一步没有成功找到方法就会执行动态方法解析,动态方法解析实际上就是动态添加方法,当然这里的方法也分为对象方法和类方法,
看代码:
- (void)eat{
NSLog(@"吃...");
}
// 动态添加实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(eat)) {
Method method = class_getInstanceMethod(self, sel);
class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
return YES;
}
return [super resolveInstanceMethod:sel];
}
+ (void)sleep{
NSLog(@"睡...");
}
// 动态添加类方法
+ (BOOL)resolveClassMethod:(SEL)sel{
if (sel == @selector(sleep)) {
Method method = class_getClassMethod(self, sel);
class_addMethod(object_getClass(self), sel, method_getImplementation(method), method_getTypeEncoding(method));
return YES;
}
return [super resolveClassMethod:sel];
}
实例对象和类对象都能调用performSelector方法
最终动态添加的方法会被添加到方法列表中。
个人认为动态方法解析在实际开发中应用并不多。
3.消息转发
看图:
0.消息转发.png
消息转发分为实例对象和类对象(完整消息转发和快速转发)。
看代码:
/*对象方法的消息转发**/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(eat)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
[anInvocation invokeWithTarget:[Son new]];
}
方法签名的作用:获取函数的参数和返回值。
/*类方法的消息转发**/
+ (id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(sleep)) {
return [Son class];
}
return [super forwardingTargetForSelector:aSelector];
}
如果这个类没有实现这个方法,就把这个消息转发给实现了这个方法(能响应这个消息)的类。
之前两篇文章里(
1.iOS开发中的消息转发应用于解决NSTimer
2.iOS开发中的内存管理之解决NSTimer造成的循环引用
)提过利用消息转发解决NSTimer循环引用的问题。
两种不同的消息转发方式有什么不同呢?
1.快速消息转发只需实现一个方法,而完整消息转发需要实现两个方法。
2.快速消息转发只能转发给一个接收者,而完整消息转发可以转发给多个接收者。
转发给多个接收者代码
// 完整消息转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(test:)) {
// return [[[Student alloc]init] methodSignatureForSelector:aSelector];
return [NSMethodSignature signatureWithObjCTypes:"i@:i"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// anInvocation.target = [[Student alloc]init];
// [anInvocation invoke];
// 转发给Student和Teacher两个接收者
[anInvocation invokeWithTarget:[[Student alloc] init]];
[anInvocation invokeWithTarget:[Teacher new]];
}