作为一个 iOS 开发者. 尤其是想向高级以及资深工程师前进的程序员. 接下来小刘发的一系列的文章都是需要掌握的东西. 你可能认为很多东西平时开发用不到, 那么请想想你和初中级程序员的区别是什么? 只有掌握原理才能在技能上有更深一步的追求. 关注我, 一起向资深iOS 开发进军.
接下来小刘将发布一系列关于 iOS 高级开发的一系列文章, 希望能和大家一起讨论,学习进步!
Demo 下载
runtime 消息转发
我们常用的对象调用方法的本质,实际是向这个对象发送一条消息.其本质是调用 c 语言的 api.
例如
//方法调用
[[Dog new] sendMessage:@"hello"];
//上面的代码等同于下面的代码
objc_msgSend([Dog new],@selector(sendMessage:),@"hello");
如果上面的Dog 类 只在.h 文件声明方法, 不在.m 文件实现. 那么我们就会遇见常见的崩溃
'-[Dog sendMessage:]: unrecognized selector sent to instance 0x600002d901d0'
那么我们为什么要认识消息转发机制呢, 它能帮助我们干什么呢. 消息转发机制的步骤又是什么?
消息转发机制的步骤可以分为三个步骤:
1.动态方法解析
2.快速转发
3.慢速转发
1.动态解析:
[[Dog new] sendMessage:@"hello"];
调用了上面的代码实际上会触发当前类的
+ (BOOL)resolveInstanceMethod:(SEL)sel
如果是类方法则会触发
+ (BOOL)resolveClassMethod:(SEL)sel
在动态解析方法的时候,首先要匹配方法的名字. 如果匹配成功, 则会动态添加为此类添加一个方法
//动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
//1.先要匹配方法 获取方法名称
NSString *methodName = NSStringFromSelector(sel);
if ([methodName isEqualToString:@"sendMessage:"]) {
/*
Class cls: 将要给添加方法的类,传的类型 [类名 class]
SEL name: 将要添加的方法名,传的类型 @selector(方法名)
IMP imp:实现这个方法的函数 ,传的类型 1,C语言写法:(IMP)方法名 2,OC的写法:class_getMethodImplementation(self,@selector(方法名:))
const char *types:表示我们要添加的方法的返回值和参数
"v@:@":v:是添加方法无返回值 @表示是id(也就是要添加的类) :表示添加的方法类型 @表示:参数类型
*/
return class_addMethod(self, sel, (IMP)sendMessage, "V@:@");
}
return NO;
}
如果此时 sendMessage 未实现,找不到这个函数. 那么就会崩溃
如果匹配不到方法, 那么返回 NO. 那么则会进入第二个步骤
2.快速转发阶段
//快速转发 找到一个备用的接受者来接收它,如果没有找到还是走它自己默认的步骤
-(id)forwardingTargetForSelector:(SEL)aSelector
{
//1.先要匹配方法 获取方法名称
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
//如果 Cat 类实现了 sendMessage:的方法,则会调用 Cat 的方法
return [Cat new];
}
return [super forwardingTargetForSelector:aSelector];
}
如果快速转发没有找到备用接收者.那么就进入第三步骤
3.慢速转发. 慢速转发又分为两个小步骤
3.1: 方法签名 把当前方法的具体信息保存起来
//方法签名
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
//1.先要匹配方法 获取方法名称
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
//利用下面的方法进行方法签名
return [NSMethodSignature signatureWithObjCTypes:"V@:@"];
}
//继续按照默认步骤走, 进入最后慢速阶段的消息转发
return [super methodSignatureForSelector:aSelector];
}
//消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = [anInvocation selector];
//找到一个备用者
Cat *cat = [Cat new];
//是否能响应方法
if ([cat respondsToSelector:sel]) {
//把当前的方法指给这个对象
[anInvocation invokeWithTarget:cat];
} else {
//继续走对应默认的步骤 指向崩溃方法
[super forwardInvocation:anInvocation];
}
}
走到最后还是没有找到消息转发者. 那么就进入一个提供报错信息的方法
//提供报错信息 重写这个方法可以防止崩溃哦
-(void)doesNotRecognizeSelector:(SEL)aSelector
{
NSLog(@"不会崩溃哦");
}
以上便是整个消息转发的流程.
那么消息转发可以帮助我们干什么呢?
我们可以在消息转发的流程里面添加代码,防止崩溃. 也可以根据自己的业务需求,在对应的阶段修改逻辑,达到我们想要的业务逻辑.
以上便是我通过多方面的学习对消息转发的理解,有问题,可以留言沟通哦
网友评论