前面已经探索了 alloc、对象初始化、内存对齐,接下来我们继续来学习 对象的本质。
一、clang 和 xcrun
clang
clang 是一个由Apple主导编写,基于LLVM的C/C++/Objective-C编译器。
因为OC是C、C++的超集,通过clang可以将m文件编译成cpp文件。
用法:
clang -rewrite-objc xx.m -o xx.cpp
xcrun
xcrun是xcode安装时候带的一个命令工具,是在clang基础上进行封装得来。
用法:
1、模拟器
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc xx.m -o xx.cpp
2、⼿机
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xx.m -o xx.cpp
二、对象本质及扩展
首先我们通过clang将下面的main.m文件编译为cpp文件:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
// 对象在底层的本质就是结构体
@interface LGPerson : NSObject
@end
@implementation LGPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Hello, World!");
}
return 0;
}
执行命令:clang -rewrite-objc main.m -o main.cpp
由于.cpp文件中有很多头文件的展开,与我们本次的探索目标无关,所以我摘录以下关键的代码:
#ifndef _REWRITER_typedef_LGPerson
#define _REWRITER_typedef_LGPerson
typedef struct objc_object LGPerson;
typedef struct {} _objc_exc_LGPerson;
#endif
//这是LGPerson对象
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;//结构体的继承,成员变量ISA
};
struct NSObject_IMPL {
Class isa;
};
//Class 是一个结构体指针,只是一个 objc_class * 的别名而已
typedef struct objc_class *Class;
struct objc_object {
Class _Nonnull isa __attribute__((deprecated));
};
//id 是 objc_object * 的别名
typedef struct objc_object *id;
typedef struct objc_selector *SEL;
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_vj_4mx0r3r94cb6lxl6pxdh225r0000gn_T_main_f586d8_mi_0);
}
return 0;
}
struct _prop_t {
const char *name;
const char *attributes;
};
struct _protocol_t;
struct _objc_method {
struct objc_selector * _cmd;
const char *method_type;
void *_imp;
};
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;
};
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 _class_t {
struct _class_t *isa;
struct _class_t *superclass;
void *cache;
void *vtable;
struct _class_ro_t *ro;
};
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
三、联合体位域拓展补充
位域:
struct LGCar1 {
BOOL front; // 0 1
BOOL back;
BOOL left;
BOOL right;
};
// 位域,节约内存
// 互斥
struct LGCar2 {
BOOL front: 1;
BOOL back : 2;
BOOL left : 6;
BOOL right: 1;
};
struct LGCar1 car1;
struct LGCar2 car2;
NSLog(@"%ld-%ld",sizeof(car1),sizeof(car2));//打印 4 1
联合体(又叫共用体):
// 共存
struct LGTeacher1 {
char *name;
int age;
double height ;
};
// 联合体 : 互斥
union LGTeacher2 {
char *name;
int age;
double height ;
};
struct LGTeacher1 teacher1;
teacher1.name = "Cooci";
teacher1.age = 18;
union LGTeacher2 teacher2;
teacher2.name = "Cooci";
teacher2.age = 18;
下断点一步步调试打印:
(lldb) p teacher1
(LGTeacher1) $0 = (name = 0x0000000000000000, age = 0, height = 0)
(lldb) p teacher1
(LGTeacher1) $1 = (name = "Cooci", age = 0, height = 0)
(lldb) p teacher1
(LGTeacher1) $2 = (name = "Cooci", age = 18, height = 0)
(lldb) p teacher2
(LGTeacher2) $5 = (name = 0x0000000000000000, age = 0, height = 0)
(lldb) p teacher2
(LGTeacher2) $6 = (name = "Cooci", age = 15964, height = 2.1220036782292425E-314)
(lldb) p teacher2
(LGTeacher2) $7 = (name = "", age = 18, height = 2.1219957998584539E-314)
总结:结构体中所有变量是“共存”的;联合体中所有变量是“互斥”的,同一时间只能有一个变量被赋值;
四、nonPointerIsa的分析
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; //关联对象 \
uintptr_t has_cxx_dtor : 1; //c++的析构函数 \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; //弱引用 \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1;//散列表 \
uintptr_t extra_rc : 8//引用计数
解析:
nonpointer:表示是否对 isa 指针开启指针优化
0:纯isa指针,1:不⽌是类对象地址,isa 中包含了类信息、对象的引⽤计数等
has_assoc:关联对象标志位,0没有,1存在
has_cxx_dtor:该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象
shiftcls:存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位⽤来存储类指针。
magic:⽤于调试器判断当前对象是真的对象还是没有初始化的空间
weakly_referenced:志对象是否被指向或者曾经指向⼀个 ARC 的弱变量,
没有弱引⽤的对象可以更快释放。
deallocating:标志对象是否正在释放内存
has_sidetable_rc:当对象引⽤技术⼤于 10 时,则需要借⽤该变量存储进位
extra_rc:当表示该对象的引⽤计数值,实际上是引⽤计数值减 1,
例如,如果对象的引⽤计数为 10,那么 extra_rc 为 9。如果引⽤计数⼤于 10,
则需要使⽤到下⾯的 has_sidetable_rc。
五、isa 推导 class
(lldb) x/4gx p
0x10054fc40: 0x011d800100008275 0x0000000000000000 //第一块是isa
0x10054fc50: 0x0000000000000000 0x0000000000000000
(lldb) p/t 0x011d800100008275
(long) $1 = 0b0000000100011101100000000000000100000000000000001000001001110101
(lldb) p/x LGPerson.class //类
(Class) $2 = 0x0000000100008270 LGPerson
(lldb) p/x 0x011d800100008275 & 0x00007ffffffffff8ULL //掩码 # define ISA_MASK 0x00007ffffffffff8ULL
(unsigned long long) $3 = 0x0000000100008270 //与类一样的地址
以上便是通过 isa 掩码 找到 cls
isa的位运算
结合 ISA_BITFIELD 的定义,通过 isa 的位运算找到 cls。
(lldb) p/x 0x011d800100008275 >> 3
(long) $9 = 0x0023b0002000104e
(lldb) p/x 0x0023b0002000104e << 20
(long) $10 = 0x0002000104e00000
(lldb) p/x 0x0002000104e00000 >> 17
(long) $11 = 0x0000000100008270
(lldb) p/x LGPerson.class
(Class) $12 = 0x0000000100008270 LGPerson