01objc_msgSend
OC中的方法调用,就是给方法的调用着发送消息,其实都是转换为objc_msgSend函数的调用
objc_msgSend的执行流程可以分为3大阶段
消息发送:给消息的接受者receiver,发送后面的那个消息,会在这个接管尝试找到这个方法然后调用,然后找到就调用成功。如果成功就不会进入后面的两步了
动态方法解析:如果消息发动阶段找不到对应的方法,就会进入这个阶段,允许开发者动态的创建一个方法,如果这个阶段,没有任何的操作,他就会进入下面的阶段
消息转发:这里有可能会转发给另一个对象来调用这个方法 。
如果这三步都找不到方法的话,objc_msgSend就会报方法找不到的错误unrecognized selector sent to instance
。
GoodStudent *goodStudent = [[GoodStudent alloc] init];
[goodStudent goodStudentTest];
这里有两个概念:
消息接收者:receiver
消息名称:goodStudentTest
[GoodStudent goodStudentName];
//我们前面执行两个方法在内存中转成的代码,
objc_msgSend)((id)goodStudent, sel_registerName("goodStudentTest"));
objc_msgSend)((id)objc_getClass("GoodStudent"), sel_registerName("goodStudentName")
第一个阶段-消息发送01
打开runtime的源码
根据治疗我们可以,参考他的执行流程
在源码的objc-msg-arm64.s里面汇编代码实现
![](https://img.haomeiwen.com/i1454864/bad0d8aae6655f7f.png)
二分查找
// 2 3 4 5 8 10 11 120 找8
先去中间的,假如他比我们要找的小就在往后找,拿后面的所有的中间带来对比,一直到我们找到这个数为止。
第一个阶段-消息发送02
![](https://img.haomeiwen.com/i1454864/765cbd997b27d33f.png)
如果是从class_rw_t中查找方法
已经排序的,二分查找
没有排序的,遍历查找
receiver通过isa指针找到receiverClass
receiverClass通过superclass指针找到superClass
04第二个阶段-动态方法解析01
进入动态方法解析之后基本的流程
![](https://img.haomeiwen.com/i1454864/f10d66511e1513d5.png)
- 开发者可以实现以下方法,来动态添加方法实现
+resolveInstanceMethod:
+resolveClassMethod:
动态解析过后,会重新走“消息发送”的流程
“从receiverClass的cache中查找方法”这一步开始执行
动态解析过后,会重新走“消息发送”的流程
“从receiverClass的cache中查找方法”这一步开始执行
一旦我们调用了没有实现的方法,他就是执行
+resolveInsatanceMethod:
+resolveClassMethod:
struct method_t{
SEL sel;
char *types;
IMP imp;
};
-(void)otherTest
{
NSLog(@"other test");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
//这里面我要做操作
if (sel == @selector(test))
{
//获取一个方法
struct method_t *other = (struct method_t *)class_getInstanceMethod(self, @selector(otherTest));
NSLog(@"test meiyoushixian");
class_addMethod(self, sel, other->imp, other->types);
}
return [super resolveInstanceMethod:sel];
}
05-动态方法解析02
使用另一种方式添加方法
Method method = class_getInstanceMethod(self, @selector(test));
//不强转来实现
class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
//或者我们自己写c函数来调用
void c_test(id self,SEL _imp)
{
NSLog(@"C函数的实现");
}
class_addMethod(self, sel, (IMP)c_test, "v16@0:8");
类方法的动态添加和对象方法相似,只不过是给元类的对象添加,所以注意添加的对象
class_addMethod(object_getClass(self), sel, (IMP)c_test, "v16@0:8");
06动态方法解析03
什么情况下会用到,平时很少用到,基本都是为了面试。
动态添加的方法也是在方法列表里面
07第三阶段-消息转发01
在前面的两步都找不到方法的实现,他就会进入消息转发阶段
08第三阶段-消息转发02
一旦进入消息转发阶段,他就就会尝试调用
forwardingTargetForSelector:
两种- +都有可能
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(eat))
{
return [[Dog alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
进入消息转发之后的源码是不开源码的,要用到逆行的知识才可以了解,大概的流程
1.先通过执行forwardingTargetForSelector, 如果返回值不为空,拿到返回值,给这个对象发消息objc_msgSend(cat, @selector(eat))
第三阶段-消息转发03.mp4
- 假如forwardingTargetForSelector没有实现,相当于返回值为空,他会调用另一个方法receiver methodSignatureForSelector:sel。也方法签名的意思也就是告诉接受者,你方法的返回值,类型是什么.
3 如果你返回的方法签名是有值的,他会将这个签名封装成一个Invocation,(包括了方法接受者,方法,参数)然后就会调用另一个方法,将invocation传入
forwardInvocation: 就可以在这个方法里处理你的操作开发者方法中自定义任何逻辑
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(eat))
{
return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// anInvocation.target 方法接受者
// anInvocation.selector 方法
// [anInvocation getArgument:NULL atIndex:0];//获取参数
Dog *dog = [Dog new];
anInvocation.target = dog;
[anInvocation invoke];
// [anInvocation invokeWithTarget:dog];
}
![](https://img.haomeiwen.com/i1454864/3d2f19e524276afc.png)
通过doesNotRecognizedSelector抛出我们常见的那个错误
10第三阶段-消息转发04
如果没有实现forwardingTargetForSelector,他也是会来到这个methodSignatureForSelector:sel->)forwardInvocation:可以在这个方法里面做任务,这里面做的事情就是我们在外面,调用方法所执行的操作
可以修改参数,修改返回值
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(eat:))
{
return [NSMethodSignature signatureWithObjCTypes:"v16@0:8i16"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
int age ;
//reciver selector other arguments
[anInvocation getArgument:&age atIndex:2];
NSLog(@"%i", age + 10); //25
}
10第三阶段-消息转发05
方法签名,没有方法签名就不会玩下走了,会报错,方法签名决定方法的参数和返回值
类方法的方法转发,
元类对象就是一种特殊的类对象,类方法的解析和转发类似于对象想方法,只不过,不类对象,换成了元类对象。不管是对象方法火类方法,转发只关心,接受者和方法名
@interface Dog : NSObject
- (void)eat;
- (void)sing;
@end
+ (id)forwardingTargetForSelector:(SEL)aSelector
{
return [[Dog alloc] init];
}
总结
介绍一下OC的消息机制
OC中的方法调用,都是转成了objc_msgSend函数的调用,其实就是给recover发送了一个消息,
objc_msgSend底层分了三大阶段
1.消息发送(当前类,父类中查找)
2.动态方法解析resolveInstanceMethod
3.消息转发
什么是runtime?平时的开发中有使用过么
@dynamic
我们平时下一个属性,他会自定帮我们生成_age, set,get的声明和实现
@dynamic age,就意味着我们不希望帮我们自动生成实现set,get不要自动生成成员变量;
消息转发可以在平时开发中用于消除,方法找不到的错误,为了防止程序的闪退率太高,我们可以自己处理一下没有实现方法的情况
网友评论