一、将Objective-C代码转换为C\C++代码
- 给项目加一个新的
target
New Run Script Phase
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
-
如果需要链接其他框架,使用
-framework
参数。比如-framework UIKit
-
为了方便查看,将生成的
main.cpp
文件加入项目中,为了避免文件冲突报错,取消参与编译的勾选项
二、OC方法调用本质
#import <Foundation/Foundation.h>
#import "ZGPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZGPerson *person = [ZGPerson new];
person.age = 10;
}
return 0;
}
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ZGPerson : NSObject
@property (nonatomic, assign) int age;
- (void)learn;
@end
NS_ASSUME_NONNULL_EN
#import "ZGPerson.h"
@implementation ZGPerson
- (void)learn {
NSLog(@"%s",__func__);
}
@end
2.1 代码反编译为C++
- 将项目代码编译成C++文件,然后拉取到最后,截图如下:
- 去掉强转条件,实际作用代码如下图:
由此我们可以得出objc_msgSend
的参数:
第一个参数:消息的接收者
第二个参数:消息的方法名(sel)
结论:OC中的方法调用,其实都是转换为objc_msgSend
函数的调用。
2.2 objc_msgSend
和它的相关类
拖动main.cpp
文件到代码顶部,我们看到objc_msgSend
有以下几个相关类:
下面环节,我们来着重了解一下objc_msgSendSuper
和objc_msgSend
的区别。
三、objc_msgSendSuper
和objc_msgSend
区别
为ZGPerson
添加一个ZGCat
子类,在main.m
文件中,初始化ZGCat类
,并调用父类属性age
。
#import "ZGPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZGCat : ZGPerson
@end
NS_ASSUME_NONNULL_END
#import "ZGCat.h"
@implementation ZGCat
- (instancetype)init
{
self = [super init];
if (self) {
NSLog(@"%@\n%@",[self class],[super class]);
}
return self;
}
@end
#import <Foundation/Foundation.h>
#import "ZGPerson.h"
#import "ZGCat.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZGCat *cat = [ZGCat new];
cat.age = 10;
}
return 0;
}
打印结果:
这里ZGCat.m
中[self class],[super class]
打印结果是一致的,都是ZGCat
,那么这里我们的self
和super
有什么区别哪?这就是我们将要探讨的内容。
3.1 代码反编译为C++
为了想要知道ZGCat.m
文件中发生了什么,我们将它反编译成C++文件(步骤同上,不再赘述)。
- 打开
ZGCat.cpp
文件,找到对应的init
方法
- 去掉强转条件,实际作用代码如下图:
objc_msgSend
第一个参数为self(消息的接收者)
,第二个参数为消息的方法名(sel)
。
objc_msgSendSuper
的第一个参数为__rw_objc_super
类型的结构体,结构体包含两个参数:(第一个参数为self(消息的接收者)
,第二个参数为消息的方法名(sel)
)。
因为消息的接收者没有改变,所以[super class]
打印出来的一样是self
,这个对象所指的类,也就是ZGCat
。
结论: objc_msgSend
是给本类发消息,objc_msgSendSuper
是给父类发消息,结果相同,但出发点不同。
3.2 自己实现一个objc_msgSendSuper
- 我们看到
[super class]
的第一个参数变成了一个_rw_objc_super
结构体,我们在源码里看一下这个结构体对应的内部结构。
- 源码中查看
objc_msgSendSuper
结构
- 具体实现代码
- (void)learn {
// [super learn];
struct objc_super zg_objc_super;
zg_objc_super.receiver = self;
zg_objc_super.super_class = ZGPerson.class;
//格式强转
void* (*learnSuper)(struct objc_super * self, SEL _cmd) = (void *)objc_msgSendSuper;
//结构注入参数
learnSuper(&zg_objc_super, @selector(learn));
}
四、objc_msgSend源码解读
在OC源码中查找objc_msgSend
,我们主要看它在arm64
中的源码实现:
- 消息接收者
receiver
为空,跳出
等同于以下代码:
void objc_msgSend(id receiver, SEL selector) {
if (receiver == nil) return;
}
- 实例对象
isa
找到class
- 查找缓存
CacheLookup
- 缓存查找或者没有缓存查找方法
_objc_msgSend_uncached
MethodTableLookup
lookUpImpOrForward
getMethodNoSuper_nolock
->search_method_list_inline
->findMethodInSortedMethodList
里的慢速查找算法
五、objc_msgSend执行流程01~消息发送
总结上文部分,我们得到objc_msgSend
第一阶段消息发送
的流程图:
下文我们会详细阐述objc_msgSend
的后两个阶段。