前言:OC 可以编译成C++,从C++的角度探索class和对象的的本质
1、把OC文件转成C++文件
生成下面的.m文件,_2415做为一个标识符
#import <Foundation/Foundation.h>
@interface Persion_2415 : NSObject
@property(nonatomic, copy) NSString * name_2415;
@property(nonatomic, assign) int age_2415;
-(int) doWork_2415:(int) week;
@end
@implementation Persion_2415
-(int) age_2415{
return self.age_2415;
}
-(int) doWork_2415:(int) week {
return 39990;
}
+(int) clDoWork_2415:(int) week {
return 500000;
}
@end
@interface Student_2415 : Persion_2415
@property(nonatomic, copy) NSString * school_2415;
@end
@implementation Student_2415
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Persion_2415 * persion_2415 = [[Persion_2415 alloc] init];
NSLog(@"%@",persion_2415);
}
return 0;
}
把OC文件生成C++ 文件
- clang -rewrite-objc main.m -o main.cpp
2、探索main.cpp
在main.cpp中查找Persion_2415
#ifndef _REWRITER_typedef_Persion_2415
#define _REWRITER_typedef_Persion_2415
typedef struct objc_object Persion_2415; //发现Persion_2415是一个struct objc_object结构
typedef struct {} _objc_exc_Persion_2415;
#endif
extern "C" unsigned long OBJC_IVAR_$_Persion_2415$_name_2415;
extern "C" unsigned long OBJC_IVAR_$_Persion_2415$_age_2415;
struct Persion_2415_IMPL { //存储Persion的数据是一个struct Persion_2415_IMPL的数据结构
struct NSObject_IMPL NSObject_IVARS;
int _age_2415;
NSString *_name_2415;
};
再看看struct objc_object的定义,搜索struct objc_object {,发现代码
typedef struct objc_class *Class; //Class是struct objc_class * 类型
struct objc_object { //struct objc_object定义
Class _Nonnull isa __attribute__((deprecated));
};
typedef struct objc_object *id; // id的定义
typedef struct objc_selector *SEL; //SEL的定义
这时候,发现了很多东西,id,SEL,Class都是结构指针,对象也是,如persion对象在OC中的写法就是Persion * persion = struct objc_object * persion,和id的定义是一样的,所以id可以代表所有对象
这时候搜索struct objc_class { 发现搜索不到struct objc_class的现实,这时在苹果提供的runtime源码(看别人介绍的)中搜索发现
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY; //isa
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE; //父类
const char * _Nonnull name OBJC2_UNAVAILABLE; //名字
long version OBJC2_UNAVAILABLE; //版本
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE; //大小
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; //成员列表
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; //方法列表
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; //缓存
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; //协议列表
#endif
} OBJC2_UNAVAILABLE;
同样在runtime源码中搜索struct objc_selector {,发现搜索不到,暂停struct objc_selector的探索
这里发现一个疑问既然对象是一个struct objc_object 结构的指针,那么怎么通过struct objc_object 指针来找到struct Persion_2415_IMPL 这里真正存数据的结构呢,暂时无法得知,停止探索
搜索一下doWork_2415 这个方法,发现:
static int _I_Persion_2415_doWork_2415_(Persion_2415 * self, SEL _cmd, int week) {
return 39990;
}
static int _C_Persion_2415_clDoWork_2415_(Class self, SEL _cmd, int week) {
return 500000;
}
原来每个类的对象方法和类方法都是一个静态的函数
还发现了:
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[8];
} _OBJC_$_INSTANCE_METHODS_Persion_2415 __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
8,
{{(struct objc_selector *)"age_2415", "i16@0:8", (void *)_I_Persion_2415_age_2415},
{(struct objc_selector *)"doWork_2415:", "i20@0:8i16", (void *)_I_Persion_2415_doWork_2415_},
{(struct objc_selector *)"name_2415", "@16@0:8", (void *)_I_Persion_2415_name_2415},
{(struct objc_selector *)"setName_2415:", "v24@0:8@16", (void *)_I_Persion_2415_setName_2415_},
{(struct objc_selector *)"setAge_2415:", "v20@0:8i16", (void *)_I_Persion_2415_setAge_2415_},
{(struct objc_selector *)"name_2415", "@16@0:8", (void *)_I_Persion_2415_name_2415},
{(struct objc_selector *)"setName_2415:", "v24@0:8@16", (void *)_I_Persion_2415_setName_2415_},
{(struct objc_selector *)"setAge_2415:", "v20@0:8i16", (void *)_I_Persion_2415_setAge_2415_}}
};
这时候有个新的结构 struct _objc_method,搜索下struct _objc_method {,发现了定义
struct _objc_method {
struct objc_selector * _cmd;
const char *method_type;
void *_imp;
};
这里有个疑问method_type的规则是什么呢,百度一下发现了一张表,但是字母后面的基地址偏移数字计算方法不清楚,但是如果需要使用到的话可以使用method_getTypeEncoding方法测试获取
贴张表(引用自网站https://blog.csdn.net/mr_yong/article/details/50379240)
image-20210617110516731.png
搜索下OBJC$_INSTANCE_METHODS_Persion_2415,查看在哪里被引用了,发现:
static struct _class_ro_t _OBJC_CLASS_RO_$_Persion_2415 __attribute__ ((used, section ("__DATA,__objc_const"))) = {
0, __OFFSETOFIVAR__(struct Persion_2415, _age_2415), sizeof(struct Persion_2415_IMPL),
(unsigned int)0,
0,
"Persion_2415",
(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_Persion_2415,
0,
(const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_Persion_2415,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Persion_2415,
};
搜索 struct _class_ro_t {,发现:
struct _class_ro_t {
unsigned int flags;
unsigned int instanceStart;
unsigned int instanceSize;
unsigned int reserved;
const unsigned char *ivarLayout;
const char *name;
const struct _method_list_t *baseMethods;
const struct _objc_protocol_list *baseProtocols;
const struct _ivar_list_t *ivars;
const unsigned char *weakIvarLayout;
const struct _prop_list_t *properties;
};
搜索struct _ivar_t {, struct _protocol_t { ,发现:
struct _protocol_t {
void * isa; // NULL
const char *protocol_name;
const struct _protocol_list_t * protocol_list; // super protocols
const struct method_list_t *instance_methods;
const struct method_list_t *class_methods;
const struct method_list_t *optionalInstanceMethods;
const struct method_list_t *optionalClassMethods;
const struct _prop_list_t * properties;
const unsigned int size; // sizeof(struct _protocol_t)
const unsigned int flags; // = 0
const char ** extendedMethodTypes;
};
struct _ivar_t {
unsigned long int *offset; // pointer to ivar offset location
const char *name;
const char *type;
unsigned int alignment;
unsigned int size;
};
搜索OBJC_CLASS_RO$_Persion_2415 在哪里被使用了发现:
extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_Persion_2415 __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_Persion_2415,
0, // &OBJC_CLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_CLASS_RO_$_Persion_2415,
};
搜索struct _class_t { 在哪里发现:
struct _class_t {
struct _class_t *isa;
struct _class_t *superclass;
void *cache;
void *vtable;
struct _class_ro_t *ro;
};
到这里class就比较清晰了,再看下main函数,可以发现方法的调用都是通过objc_msgSend 这个函数的
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
Persion_2415 * persion_2415 = ((Persion_2415 *(*)(id, SEL))(void *)objc_msgSend)((id)((Persion_2415 *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Persion_2415"), sel_registerName("alloc")), sel_registerName("init"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2k_hpcfw7594615f89pv2ydfbmh__2sjv_T_main_c2be56_mi_0,persion_2415);
}
return 0;
}
总结:
-
objc对象和id类型在C++中都是struct objc_object * 结构体指针
-
类的成员方法和类方法的实现都是static 函数,有两个默认参数self, SEL _cmd
-
类方法和对象方法的调用都是通过 objc_msgSend 来调用的
疑问:
objc对象 是struct objc_object *的指针,但是struct objc_object 结构中只保存的类的信息,对象的信息存储是在Persion_2415_IMPL里的,那么是怎么关联到Persion_2415_IMPL的呢?
网友评论