- 对象的本质是结构体
- 方法的本质是消息发送
对象的本质
首先有一个SPPerson
对象
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
SPPerson *p = [SPPerson new];
}
return NSApplicationMain(argc, argv);
}
我们知道OC底层是C和C++的汇编,我们用命令可以编译为C++代码
clang -rewrite-objc main.m -o main.cpp
// 更加优秀的编译是根据平台 和架构来
/**
xcrun : xcode run
-sdk iphoneos : iphoneos操作系统
-arch arm64 : arm64架构
*/
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
这里把main.m
文件编译为main.cpp
文件(十多万行代码,上面都是环境啊配置相关的内容,我们不关心,直接拖到最下面)
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
SPPerson *p = ((SPPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("SPPerson"), sel_registerName("new"));
}
return NSApplicationMain(argc, argv);
}
全局查找SPPerson
#ifndef _REWRITER_typedef_SPPerson
#define _REWRITER_typedef_SPPerson
typedef struct objc_object SPPerson;
typedef struct {} _objc_exc_SPPerson;
#endif
struct SPPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
};
很明显的一句话typedef struct objc_object SPPerson;
那么我们的SPPerson
就是objc_object
类型的结构体~~~那么我们所说的对象预计都是objc_object
类型的结构体!我们带着好奇心继续查看objc_object
,(搜索struct objc_object {
)
struct objc_object {
Class _Nonnull isa __attribute__((deprecated));
};
非常熟悉的身形isa
这个时候我就想到了另外一个东西NSObject
#ifndef _REWRITER_typedef_NSObject
#define _REWRITER_typedef_NSObject
typedef struct objc_object NSObject;
typedef struct {} _objc_exc_NSObject;
#endif
struct NSObject_IMPL {
Class isa;
};
我们的NSObject
的isa
是objc_object
结构体中的属性在我们实现NSObject
的时候只是对isa
的重写~~~isa
出镜率很高,比如我们编译block
也能看到isa
,代表这个匿名函数也是对象,也重复符合万物皆对象
(别说你还缺对象)的说法!!!
方法的本质
继续来看方法的本质
SPPerson *p = [SPPerson new];
//clang -rewrite-objc main.m -o main.cpp
SPPerson *p = ((SPPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("SPPerson"), sel_registerName("new"));
调用SPPerson的new方法进行初始化,通过编译的代码我们发现底层是通过一个函数objc_msgSend()就是消息发送方法,
objc_msgSend(id self, SEL _cmd);
- 一个是消息接受者
- 一个是消息编号
我们在代码中直接调用objc_msgSend()方法时,系统提示默认参数个数是0个,考虑到编译参数问题,可以关闭严格核查,这样就不会报错了。
我通过SEL能找到函数实现,底层是依赖一个IMP的函数指针
就会找我们具体的函数实现
下面我模拟是不是也可不断发送消息,模拟四种消息发送
/*
场景:
1.两个类SPPerson和SPStudent,SPStudent继承自SPPerson;
2.两个类.h文件都声明对象方法run和类方法walk
3..m文件中实现方法
- (void)run {
NSLog(@"%s", __func__);
}
+ (void)walk {
NSLog(@"%s", __func__);
}
*/
SPStudent *s = [[SPStudent alloc] init];
[s run];
// 方法调用底层编译
// 方法的本质: 消息 : 消息接受者 消息编号 ....参数 (消息体)
objc_msgSend(s, sel_registerName("run"));
// 类方法编译底层
[SPStudent walk];
objc_msgSend(objc_getClass("SPStudent"), sel_registerName("walk"));
// 向父类发送消息(对象方法)
struct objc_super mySuper;
mySuper.receiver = s;
mySuper.super_class = class_getSuperclass([s class]);
objc_msgSendSuper(&mySuper, @selector(run));
// 向父类发送消息(类方法)
struct objc_super myClassSuper;
myClassSuper.receiver = s;
myClassSuper.super_class = class_getSuperclass(object_getClass([s class]));
objc_msgSendSuper(&myClassSuper, sel_registerName("walk"));
打印结果
2019-11-05 22:41:34.628826+0800 atys[21534:2150951] -[SPStudent run]
2019-11-05 22:41:34.628964+0800 atys[21534:2150951] -[SPStudent run]
2019-11-05 22:41:34.629047+0800 atys[21534:2150951] +[SPStudent walk]
2019-11-05 22:41:34.629128+0800 atys[21534:2150951] +[SPStudent walk]
2019-11-05 22:41:34.629214+0800 atys[21534:2150951] -[SPPerson run]
2019-11-05 22:41:34.629292+0800 atys[21534:2150951] +[SPPerson walk]