Clang介绍
Clang是一个C语言、C++、Objective-C语言的轻量级编译器。源代码发布于BSD协议下。 Clang将支持其普通lambda表达式、返回类型的简化处理以及更好的处理constexpr关键字。 2013年4月,Clang已经全面支持C++11标准,并开始实现C++1y特性(也就是C++14,这是 C++的下一个小更新版本)。Clang将支持其普通lambda表达式、返回类型的简化处理以及更 好的处理constexpr关键字。 [2] Clang是一个C++编写、基于LLVM、发布于LLVM BSD许可证下的C/C++/Objective-C/ Objective-C++编译器。它与GNU C语言规范几乎完全兼容(当然,也有部分不兼容的内容, 包括编译命令选项也会有点差异),并在此基础上增加了额外的语法特性,比如C函数重载 (通过__attribute__((overloadable))来修饰函数),其目标(之一)就是超越GCC。
说了这么多,咱也看不懂是啥,总之记住“Clang是由Apple主导编写,基于LLVM的C/C++/Object-C的编绎器,它可以将我们的C/C++/OC代码编绎成.cpp的C++文件”,那么怎么编绎呢?
使用 **clang -rewrite-objc main.m -o main.cpp **将main.m文件编绎成main.cpp文件
第一步:cd到文件当前的目录
第二步:输入指令,并回车
第三步:来到当前文件所在的目录
这样我们就成功的将main.m文件编绎成了main.cpp文件
使用 ** clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.2.0 -isysroot/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.2.sdk ViewController.m ** 将UIKit的文件编绎成.cpp文件
使用流程同上,有一些注意的地方:"13.2.0"是当前XCode的sdk版本号,如果你不自己是多少,可以通过路径查找Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.2.sdk,编绎结果如下:
注意
xcode安装的时候顺带安装了xcrun命令,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 main- arm64.cpp (手机)
验证OC对象的本质是结构体
打开刚刚编绎的main.cpp文件,并全局搜索LGPerson
LGPerson继承自NSObject,有name和age两个属性,在其结构体中也得到了体现;NSObject_IMPL就是NSObject编绎后的结构体。
至此,我们就成功的证明了OC对象结构体的本质
通过OC之alloc底层原理我们可以知道,对象的内存在开辟后会跟类进行关联,关联的过程会创建isa,那么这个过程是什么样?isa的结构是什么样的?
指针地址关联类对象及isa的结构分析
首先我们打开一份可编绎的OC底层实现原码,找到initInstanceIsa的实现
跟到initIsa的实现
点开newisa.bits
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
};
这样我们就来了isa的联合体结构,其中cls是类对象,cls和bits是互斥的,它们两个只有一个会被赋值
我们点开ISA_BITFIELD
于是我们看到了__x86_64__和__arm64__两种结构的isa结构
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# 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 deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
下面解释一下isa结构中每一个位置的意思:
- 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。
验证shiftcls存的确实是类
运行如下代码,并断点
lldb调试
因为是在Mac上运行的,执行的是模拟器的isa结构
其中,从右到左的第3个->左47个是shiftcls
- x/4gx person:以十六进制打印person的4段内存情况,其中0x001d80010000220d为isa指针
- 将右边三个bit和左边17个bit清空,得到shiftcls
- (lldb) p/x 0x001d80010000220d>>3
- (long) $2 = 0x0003b00020000441
- (lldb) p/x 0x0003b00020000441<<20
- (long) $3 = 0x0002000044100000
- (lldb) p/x 0x0002000044100000>>17
- (long) $4 = 0x0000000100002208
- po 0x0000000100002208打印最终得到的
至些我们验证的shiftcls存储的确实就是类信息
其实我们还可以直接通过ISA_MASK:0x00007ffffffffff8来从isa中打印出类信息
对象isa指针的内存分布如下图:
** 总结:通过alloc创建对象时,通过instanceSize计算出对象所占的内存大小,再使用calloc为对象分配内存空间并返回一个指针地址,然后通过initInstanceIsa和initIsa将这个指针地址和类进行关联,这个指针地址就是isa。isa的内存结构分为两种:__x86_64__ 和 __arm64__。isa 的创建方式包含nonpointer方式和非nonpointer方式,其中系统自带的类创建的对象是非nonpointer,自定义的类创建的结象是nonpointer。 **