oc代码在编写完成后,编译器会将源码编译成可执行程序。apple使用的编译器统称llvm。其中编译前段的工具是clang。
clang -rewrite-objc XXX.m 是clang工具中提供了一个将oc源码转为 c 代码的工具。今天就是来学习一下oc在编译成c++的具体形式以及和objc源码中的对应关系。
更多内容请查看
OC的类在编译时经过哪些步骤
为了编译情况,需要了解一些常见的 clang 命令
// 查看编译源文件需要的几个不同的阶段:yo
$ clang -ccc-print-phases LGPerson.m
0: input, "LGPerson.m", objective-c //
1: preprocessor, {0}, objective-c-cpp-output // 预处理
2: compiler, {1}, ir //编译器前端生成IR中间代码
3: backend, {2}, assembler // 编译器后端生成汇编代码
4: assembler, {3}, object // 生成目标代码
5: linker, {4}, image // 链接一些动态库
6: bind-arch, "x86_64", {5}, image //生成适合某个架构的代码
// 将.m 文件编译成.cpp文件 对应上面的 1: preprocessor, {0}, objective-c-cpp-output
$ clang -rewrite-objc LGPerson.m
$ clang -rewrite-objc LGPerson.m 编译之后的cpp文件查看类的信息
//可以根据类定义的名称LGPerson去查询,找到了定义
struct objc_object {
Class _Nonnull isa __attribute__((deprecated));
};
typedef struct objc_object LGPerson; // 可以看出LGPerson就只有一个isa的指针,类似于id的返回
struct _class_t { // 对应与objc原码中的 objc_class
struct _class_t *isa; // isa指针
struct _class_t *superclass; // 父类
void *cache; // 方法缓存(用于快速查找方法)
void *vtable; // ..
struct _class_ro_t *ro; // 数据的存储
};
struct objc_class : objc_object { // objc_object是只含一个isa指针的结构体
// Class ISA; // 在objc_object结构体中定义了
Class superclass;
cache_t cache; // formerly cache pointer and vtable 是之前的cache和vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags (根据class_ro_t生成的 class_rw_t的信息)
...
}
// 也可以根据查到 _class_t类型的 OBJC_CLASS_$_LGPerson 定义。
extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_LGPerson __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_LGPerson, // 元类
0, // &OBJC_CLASS_$_NSObject, // 父类
0, // (void *)&_objc_empty_cache, // 缓存
0, // unused, was (void *)&_objc_empty_vtable, //
&_OBJC_CLASS_RO_$_LGPerson,
};
可以看出类的内存:
- isa指针
- supercls指针
- 用于存储方法缓存的cache
- 类的实例信息如:实例成员变量,实例方法,协议等
class_t中的 cache
在OBJC_CLASS_SETUP_$_LGPerson LGPerson的赋值函数中。
cache被赋值了_objc_empty_cache
static void OBJC_CLASS_SETUP_$_LGPerson(void ) {
OBJC_METACLASS_$_LGPerson.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_LGPerson.superclass = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_LGPerson.cache = &_objc_empty_cache;
OBJC_CLASS_$_LGPerson.isa = &OBJC_METACLASS_$_LGPerson;
OBJC_CLASS_$_LGPerson.superclass = &OBJC_CLASS_$_NSObject;
OBJC_CLASS_$_LGPerson.cache = &_objc_empty_cache;
}
// _objc_empty_cache 的定义 结构体objc_cache 的一个实例.因该是一个空的结构体。主要用来保存方法缓存。具体可以查看 发布方法查找的源码
extern "C" __declspec(dllimport) struct objc_cache _objc_empty_cache;
// 如果LGPerson 继承自Person
static void OBJC_CLASS_SETUP_$_LGPerson(void ) {
OBJC_METACLASS_$_LGPerson.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_LGPerson.superclass = &OBJC_METACLASS_$_Person;
OBJC_METACLASS_$_LGPerson.cache = &_objc_empty_cache;
OBJC_CLASS_$_LGPerson.isa = &OBJC_METACLASS_$_LGPerson;
OBJC_CLASS_$_LGPerson.superclass = &OBJC_CLASS_$_Person;
OBJC_CLASS_$_LGPerson.cache = &_objc_empty_cache;
}
可以得知:对象的isa只想类。类的isa指向对应的 mataclass。 而mataclass的isa 则直接只想 NSObject的mataClass。同样NSObject的mataClass 的isa指向自己。
isa指向
相关参考和验证:isa的探究
- 实例对象的isa指向对应的类
- 类的isa指向对应的 mataclass
- mataclass的isa 则直接只想 NSObject的mataClass。SObject的mataClass 的isa只想自己
superclass
- 类的superclass 指向他的父类。
- NSObject的superclass 位0x01 位nil
OBJC_CLASS_RO$_LGPerson
存储着累定义的成员变量,属性,方法,协议数据。相当于源码中的class_ro_t。 由此可以理解 class_ro_t 是编译过程中已经确定的了,可以计算类的空间大小。
// 类实例的结构体,以及可以用来计算类实例的大小。成员对应的顺序和属性定义的顺序并不相同。做了结构体的内存优化,但是只有属性做了优化
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int a_a;
char b_b;
NSString *c_c;
int _bbbbb;
int _ccccc;
NSString * _Nonnull _aaaaaa;
NSString * _Nonnull _ddddd;
NSString * _Nonnull _eeeee;
};
// 主要存储着类的原始信息。 对应OC源码中的 class_ro_t
struct _class_ro_t {
unsigned int flags;
unsigned int instanceStart; // 首页成员变量的偏移位置
unsigned int instanceSize; // 实力对象的大小
unsigned int reserved; // 源码中有着条件 #ifdef __LP64__ 及其是否64位
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;
};
// 编译是确定的类的数据
static struct _class_ro_t _OBJC_CLASS_RO_$_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
0,
__OFFSETOFIVAR__(struct LGPerson, a_a), // instanceStart a_a是第一个属性
sizeof(struct LGPerson_IMPL), // instanceSize 类实例的大小计算 结构体的大小
(unsigned int)0, // reserved
0, // ivarLayout
"LGPerson", // 类名
(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_LGPerson, // 方法的列表
0, // 协议列表
(const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_LGPerson,// 成员变量列表。所有的变量
0, //
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_LGPerson, // 属性列表 使用property修饰的
};
// _objc_empty_vtable的定义可以在源码中找到。是一个的IMP类型(代码块闭包类型)
OBJC_EXPORT IMP _Nonnull _objc_empty_vtable
struct LGPerson_IMPL 可以看作一个实例对象的世纪内存:
- 在源码中最终是继承 objc_object。包含有一个isa指针
- 包含父类对象的成员变量
- 自身的成员变量
- 属性的定义的顺序被优化,不是按照定义的顺序参考结构体的内存布局。
- 实例对象的大小 instanceSize 是根据这个struct来计算的。sizeof(struct LGPerson_IMPL),
OBJC$_INSTANCE_VARIABLES_LGPerson 成员变量列表
属性会在数量列表中,也会在成员列表中。另外属性也会生成对应的get/set方法。根据修饰词的不同,调用objc_setProperty 并传入不同的参数
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count;
struct _ivar_t ivar_list[8];
} _OBJC_$_INSTANCE_VARIABLES_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_ivar_t), // 对应的 entsize 大小
8, // 8 count 成员变量数量
{{(unsigned long int *)&OBJC_IVAR_$_LGPerson$a_a, "a_a", "i", 2, 4}, // int 对应1 4个字节
{(unsigned long int *)&OBJC_IVAR_$_LGPerson$b_b, "b_b", "c", 0, 1}, // char 对应0 1个字节
{(unsigned long int *)&OBJC_IVAR_$_LGPerson$c_c, "c_c", "@\"NSString\"", 3, 8}, // NSString 对应3 8个字节
{(unsigned long int *)&OBJC_IVAR_$_LGPerson$_bbbbb, "_bbbbb", "i", 2, 4},
...
};
OBJC$_PROP_LIST_LGPerson property修饰的属性列表
static struct /*_ivar_list_t*/ {
// property 修饰的属性列表
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[5];
} _OBJC_$_PROP_LIST_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
5,
{{"aaaaaa","T@\"NSString\",&,N,V_aaaaaa"},
{"bbbbb","Ti,N,V_bbbbb"},
{"ccccc","Ti,N,V_ccccc"},
{"ddddd","T@\"NSString\",&,V_ddddd"},
{"eeeee","T@\"NSString\",C,N,V_eeeee"}}
};
//例如
@property (copy, nonatomic)NSString * eeeee;
// 对应的生成的get/set方法
static NSString * _Nonnull _I_LGPerson_eeeee(LGPerson * self, SEL _cmd) { return (*(NSString * _Nonnull *)((char *)self + OBJC_IVAR_$_LGPerson$_eeeee)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_LGPerson_setEeeee_(LGPerson * self, SEL _cmd, NSString * _Nonnull eeeee) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _eeeee), (id)eeeee, 0, 1); } // 0 对应是 是否atomic。 1对应的是 是否 copy
// get方法是直接读取对象对应偏移位置的属性
static NSString * _Nonnull _I_LGPerson_eeeee(LGPerson * self, SEL _cmd) { return (*(NSString * _Nonnull *)((char *)self + OBJC_IVAR_$_LGPerson$_eeeee)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
// objc_setProperty 源码 cmd就是可以自定义方法名,offset是变量偏移值。 还有是否原子操作以及是否 copy
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
bool mutableCopy = (shouldCopy == MUTABLE_COPY);
reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}
// 具体的设置方法
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
OBJC$_INSTANCE_METHODS_LGPerson 方法列表
在oc中定义的方法,生成了对应的C函数。第一个参数是对象地址。然后会被存储到class_ro_t 的方法列表中
static instancetype _I_LGPerson_init(LGPerson * self, SEL _cmd) {
self = ((LGPerson *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("LGPerson"))}, sel_registerName("init"));
if (self) {
(*(int *)((char *)self + OBJC_IVAR_$_LGPerson$a_a)) = 100;
((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)self, sel_registerName("setAaaaaa:"), (NSString *)&__NSConstantStringImpl__var_folders_gw_flhw88953gg0m9rcpg_y974w0000gn_T_LGPerson_35da91_mi_0);
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)self, sel_registerName("setBbbbb:"), 10);
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)self, sel_registerName("setCcccc:"), 101);
((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)self, sel_registerName("setDdddd:"), (NSString *)&__NSConstantStringImpl__var_folders_gw_flhw88953gg0m9rcpg_y974w0000gn_T_LGPerson_35da91_mi_1);
}
return self;
}
static void _I_LGPerson_test(LGPerson * self, SEL _cmd) {
LGPerson * test = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc")), sel_registerName("init"));
sizeof(&test);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_gw_flhw88953gg0m9rcpg_y974w0000gn_T_LGPerson_35da91_mi_2);
}
// 方法列表
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[23];
} _OBJC_$_INSTANCE_METHODS_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
23,
{{(struct objc_selector *)"init", "@16@0:8", (void *)_I_LGPerson_init}, // C函数的地址
{(struct objc_selector *)"test", "v16@0:8", (void *)_I_LGPerson_test},
{(struct objc_selector *)"dealloc", "v16@0:8", (void *)_I_LGPerson_dealloc},
{(struct objc_selector *)"aaaaaa", "@16@0:8", (void *)_I_LGPerson_aaaaaa},
{(struct objc_selector *)"setAaaaaa:", "v24@0:8@16", (void *)_I_LGPerson_setAaaaaa_},
...
};
成员变量
vtable
_class_t 的总结
_class_t是类的结构体。有四个成员
- isa是mataclass地址。
- superclass 是父类地址。
- cache 缓存的地址
- vtable //
- _class_ro_t 类编译期间确定的数据。比如成员变量,方法列表等。
struct _class_ro_t {
unsigned int flags;
unsigned int instanceStart;
unsigned int instanceSize;
unsigned int reserved; // #ifdef __LP64__
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;
};
// 对应objc源码class_ro_t
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
...
}
static struct _class_ro_t _OBJC_CLASS_RO_$_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
0,
__OFFSETOFIVAR__(struct LGPerson, _age),
sizeof(struct LGPerson_IMPL),
(unsigned int)0,
0,
"LGPerson",
(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_LGPerson,
0,
(const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_LGPerson,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_LGPerson,
};
属性偏移量的计算
#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)
typedef struct objc_object LGPerson;
extern "C" unsigned long int OBJC_IVAR_$_LGPerson$a_a __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct LGPerson, a_a);
// define 替换之后
((long long) &((struct LGPerson *)0)->a_a)
//
((long long) &((struct objc_object *)0)->a_a)
总结
使用 clang -rewrite-objc 名称生成oc类对应的编译的cpp文件,主要是为了配合源码学习和了解类的属性,成员变量,方法,协议是怎么存储以及对应到oc源码中的。
- 实例,类,mata的isa和superclass 指向
- 类的存储结构布局 _class_t 的数据 isa,supercls, cache,vtable,class_ro_t
- class_ro_t中存储了类的flag,首位偏移量,实例对象内存的大小(在alloc中有过介绍),表方法列表,成员变量列,协议列表
- 类会生成对应的mataclass。其中存储着对应的类变量,类方法
- 类和元类在编译器的数据存储,协议的定义(方法,协议的存储)