准备
-
想要了解本质,需要用
Clang将OC的类编译为底层的C/C++ -
Clang是基于LLVM的C/C++/Object-C的编译器,相当于是LLVM的前端 -
把⽬标
main.h⽂件编译成c++⽂件
clang -rewrite-objc main.m -o main.cpp
- UIKit报错问题:
注意SDK版本
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot /
Applications/Xcode.app/Contents/Developer/Platforms/
iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m
xcrun命令在clang的基础上进⾏了⼀些封装,要更好⽤⼀些
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o
main-arm64.cpp (模拟器)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o mainarm64.cpp (⼿机)
对象的本质及拓展
什么是对象
- 用
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc WLWPerson.m -o WLWLPerson.cpp将WLWPerson.m编译生成WLWPerson.cpp
- 打开
WLWPerson.cpp, 发现代码非常的长,很难阅读。我们可以根据自己熟悉的类进行搜索定位,检索WLWPerson
- 可以看出
WLWPerson实际是objc_object,对象的本质是objc_object结构体
class类型objc_class* Class, 是一个结构体的指针
id同样也是objc_object *指针,所以用id的时候不需要加*
- 生成对应的
set, get方法,我们能看到其中的隐藏参数self
- 这是对应的
get方法,我们发现取值的时候是通过self内存平移取值的,这也表明了对象内存排列,第一个是isa,然后成员变量一次排列
联合体位域
struct WLWCar1 {
BOOL front; // 0 1
BOOL back;
BOOL left;
BOOL right;
};
struct WLWCar2 {
BOOL front: 1;
BOOL back : 1;
BOOL left : 1;
BOOL right: 1;
};
// 共存
struct WLWTeacher1 {
char *name;
int age;
double height ;
};
// 联合体 : 成员变量互斥,共享内存
union WLWTeacher2 {
char *name;
int age;
double height ;
};
WLWCar1内部有用4个bool, 占用4字节,也就32位,为了避免浪费,我们可以用一个字节来表示,一个字节8位,我们用每一位表示一个boolWLWCar2一个字节,二进制表示0000 1111,:后边表示占用几位- 结构体是共存的,所有成员变量都能够同时赋值
- 联合体则不同,成员变量之间的赋值的是互斥的,也就是只能一个有值,比如赋值了
name,再赋值age,那么name就没有值了,因为他们公用一片内存空间,最后赋值的会将之前给替换段 - 联合体可以节省内存空间
nonpointerIsa分析
- 对象
isa的初始化,可以看出isa是isa_t类型
isa_t是一个联合体,成员变量之间是互斥的defined(ISA_BITFIELD)宏,是否定义了ISA_BITFIELD, 查看发现在不同的架构定义个不相同,但大致都差不多
// __has_feature(ptrauth_calls) 只有在 A12 以上才有 arm64e
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# define ISA_MASK 0x007ffffffffffff8ULL // 获取 cls 的掩码
# define ISA_MAGIC_MASK 0x0000000000000001ULL // 获取 MAGIC 的掩码
# define ISA_MAGIC_VALUE 0x0000000000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 0
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; // 是否 nonpointer \
uintptr_t has_assoc : 1; // 标记优化isa指针 \
uintptr_t weakly_referenced : 1; // 是否被弱引用,没有则更快释放 \
uintptr_t shiftcls_and_sig : 52; // 类和签名 \
uintptr_t has_sidetable_rc : 1; // 引用计数是否存到了 sidetable 中 \
uintptr_t extra_rc : 8 // 存储该对象的引用计数值减一后的结果, 如果够存则存在这里,不够则存在 sidetable
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; // 是否 nonpointer \
uintptr_t has_assoc : 1; // 标记优化isa指针 \
uintptr_t has_cxx_dtor : 1; // 是否有C++析构函数,如果有则走析构逻辑,没有则更快释放 \
uintptr_t shiftcls : 33; // 类 /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; // 用于判断对象是否已经完成了初始化 \
uintptr_t weakly_referenced : 1; // 是否被弱引用,没有则更快释放 \
uintptr_t unused : 1; // 是否正在被释放 \
uintptr_t has_sidetable_rc : 1; // 如上 \
uintptr_t extra_rc : 19 // 如上
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# endif∫
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
- 为了避免浪费内存,我们可以看到
isa指针如果是nonapointerIsa,64位存了很多的,不单单只是一个指针
isa推导Class
首先初始化一个对象p1然后打上断点,然后通过lldb调试
- 我们已经知道的对象内部的排列,第一个变量就是我们的
isa - 我们也知道
isa就是isa_t联合体,内部存了类,是否弱引用,引用计数等一些 - 然后我们看到
ISA_MASK 0x007ffffffffffff8ULL掩码,我们通过isa与上掩码来得到我们的shiftCls也就是类 - mask 打个比方就是一个面具,与上之后,漏出你想要的内容
- 通过打印也可是看出对象
sia中类地址,就是我们WLWPerson的类的地址 initIsa就是对isa位域的赋值- 如果不是
nonapointerIsa,isa存的就是class的地址