消息发送
[person read:book];
objc_msgSend(person, @selector(read:), book);
objc_msgSend的具体流程如下:
- 通过isa指针找到所属类
- 查找类的cache列表, 如果没有则下一步
- 查找类的”方法列表”
- 如果能找到与选择子名称相符的方法, 就跳至其实现代码
- 找不到, 就沿着继承体系继续向上查找
- 如果能找到与选择子名称相符的方法, 就跳至其实现代码
- 找不到, 执行”消息转发”.
原则就是从当前类开始查找方法的实现,找不到就去父类找,父类找不到就去父类的父类找,一直找到根类,如果最终还是没有找到,则执行消息转发。
消息转发
消息转发的整个流程为三步。
- 动态方法解析。
//实例方法
+ (BOOL)resolveInstanceMethod:(SEL)selector;
//类方法
+ (BOOL)resolveClassMethod:(SEL)selector;
- 找备用接收者
- (id)forwardingTargetForSelector:(SEL)selector;
- 消息签名和消息转发。
//消息签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
//消息转发
- (void)forwardInvocation:(NSInvocation *)invocation;
如果以上都无法处理消息的话,则会抛出异常。可以通过对下面方法的处理避免崩溃。
- (void)doesNotRecognizeSelector:(SEL)aSelector;
20160906224709111.png
代码实现例子
在Person类中定一个一个方法- (void)sendMessage:(NSString *)message;,但并没有去实现这个方法。
在Dog类中定义并实现了了- (void)sendMessage:(NSString *)message;
通过Person类的对象调用sendMessage:方法,来模拟实现消息的转发机制流程。
外部调用
Person *p = [[Person alloc]init];
[p sendMessage:@"哈哈"];
Person头文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
- (void)sendMessage:(NSString *)message;
@end
NS_ASSUME_NONNULL_END
Dog文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Dog : NSObject
- (void)sendMessage:(NSString *)message;
@end
NS_ASSUME_NONNULL_END
#import "Dog.h"
@implementation Dog
- (void)sendMessage:(NSString *)message
{
NSLog(@"备用接收者实现:%@",message);
}
@end
Person.m文件中的实现。
当Person调用sendMessage方法时,因为person并没有实现这个方法,因此按照继承树层级查找查找不到该方法的实现。则会走第一个动态解析的步骤,在这一步,我们可以选择是否动态的去添加一下这个方法的实现,从而结束消息转发的流程,如果不解决这个问题,则会继续走下一步。
//1.动态方法解析
//是否要添加这个方法的实现,添加后这个方法就会实现,如果不添加则走第二步,越往后处理这个事件,消耗也是越来越大
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *methodName = NSStringFromSelector(sel);
if ([methodName isEqualToString:@"sendMessage:"]) {
return class_addMethod([self class],sel, (IMP)sendMessage, "v@:@");
}
return NO;
}
void sendMessage(id self, SEL cmd, NSString *message){
NSLog(@"%@",message);
}
当不在动态添加方法的情况下,消息转发继续想下一步执行,会去寻找备用者,也就是返回另一个类的对象,用这个对象去解决这个方法的实现问题。
//2.找备用的接收者
//转发到备用的接受者,让备用的接受者处理。
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [Dog new];
}
//如果没有找到备用者,就走继承树
return [super forwardingTargetForSelector:aSelector];
}
当找不到备用接收者的时候,则会进入到第三步,第三步分为两个小步,第一步先进行方法签名,第二步进行消息转发,将消息转发给其他对象。
//3. 1.方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
//3. 2.消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL sel = [anInvocation selector];
Dog *dog = [Dog new];
//如果dog实现了这个方法,则把这个消息转发给dog,如果没实现,则走继承树
if ([dog respondsToSelector:sel]) {
[anInvocation invokeWithTarget:dog];
}else {
[super forwardInvocation:anInvocation];
}
[super forwardInvocation:anInvocation];
}
如果以上都无法处理这个消息,则消息无法处理,会产生崩溃,此时我们重写下面方法,则会解决崩溃问题。
// 消息无法处理的情况 处理过后不会崩溃
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
NSLog(@"消息无法处理");
}
网友评论