iOS开发的语⾔不管是 OC 还是 Swift 后端都是通过 LLVM 进⾏编译的,如下图所示:
LLVM.png
可以看到:
OC 通过 clang 编译器,编译成IR,然后再⽣成可执⾏⽂件.o(这⾥也就是我们的机器码)
Swift 则是通过 Swift编译器 编译成IR,然后在⽣成可执⾏⽂件。
我们再来看⼀下,⼀个 ⽂件的编译过程都经历了哪些步骤:
swift 在编译过程中使⽤的前端编译器是 swiftc ,和我们之前在 OC 中使⽤的 Clang 是有所区别的。 我
们可以通过如下命令查看 swiftc 都能做什么样的事情:
swiftx -h
USAGE: swiftc
MODES:
-dump-ast Parse and type-check input file(s) and dump AST(s)
-dump-parse Parse input file(s) and dump AST(s)
-dump-pcm Dump debugging information about a precompiled Clang module
-dump-scope-maps <expanded-or-list-of-line:column>
Parse and type-check input file(s) and dump the scope map(s)
-dump-type-info Output YAML dump of fixed-size types from all imported modules
-dump-type-refinement-contexts
Type-check input file(s) and dump type refinement contexts(s)
-emit-assembly Emit assembly file(s) (-S)
-emit-bc Emit LLVM BC file(s)
-emit-executable Emit a linked executable
-emit-imported-modules Emit a list of the imported modules
-emit-ir Emit LLVM IR file(s)
-emit-library Emit a linked library
-emit-object Emit object file(s) (-c)
-emit-pcm Emit a precompiled Clang module from a module map
-emit-sibgen Emit serialized AST + raw SIL file(s)
-emit-sib Emit serialized AST + canonical SIL file(s)
-emit-silgen Emit raw SIL file(s)
-emit-sil Emit canonical SIL file(s)
-index-file Produce index data for a source file
-parse Parse input file(s)
-print-ast Parse and type-check input file(s) and pretty print AST(s)
-resolve-imports Parse and resolve imports in input file(s)
-typecheck Parse and type-check input file(s)
SIL分析
class Teacher{
var age:Int = 18
var name:String = "abc"
}
var t = Teacher() //OC:alloc(内存分配) init(初始化)
通过swiftc -emit-sil main.swift >> ./main.sil
编译
// t
sil_global hidden @$s4main1tAA7TeacherCvp : $Teacher
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
alloc_global @$s4main1tAA7TeacherCvp // id: %2
%3 = global_addr @$s4main1tAA7TeacherCvp : $*Teacher // user: %7
%4 = metatype $@thick Teacher.Type // user: %6
// function_ref Teacher.__allocating_init()
%5 = function_ref @$s4main7TeacherCACycfC : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %6
%6 = apply %5(%4) : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %7
store %6 to %3 : $*Teacher // id: %7
%8 = integer_literal $Builtin.Int32, 0 // user: %9
%9 = struct $Int32 (%8 : $Builtin.Int32) // user: %10
return %9 : $Int32 // id: %10
} // end sil function 'main'
这里粘贴了些主要内容
- @main 这⾥标识我们当前 main.swift 的⼊⼝函数,SIL 中的标识符名称以 @ 作为前缀
- %0, %1... 在 SIL 也叫做寄存器,这⾥我们可以理解为我们⽇常开发中的常量,⼀旦赋值之后就不可以再修改,如果 SIL 中还要继续使⽤,那么就不断的累加数字。 同时这⾥所说的寄存器是虚拟的,最终运⾏到我们的机器上,会使⽤真的寄存器。
-
alloc_global
创建⼀个全局变量 -
global_addr
拿到全局变量的地址,赋值给 %3 -
metatype
拿到Teacher
的Metadata
赋值给 %4 - 将
allocation_init
的函数地址赋值给 %5 -
apply
调⽤allocation_init
, 并把返回值给 %6 - 将 %6 的值存储到 %3(也就是我们刚刚创建的全局变量的地址)
- 构 建 Int , 并 return
alloc_global @$s4main1tAA7TeacherCvp
定义一个全局的变量,我们可以通过命令xcrun swift-demangle s4main1tAA7TeacherCvp
查看其实就是下面
$s4main1tAA7TeacherCvp ---> main.t : main.Teacher
%0
,%1
相当于虚拟的寄存器。相当于常量。所以这段意思也就是5%为初始化方法,将元类型放进去,然后得出结果6%放入全局变量3%。初始化一个实例变量地址赋值给全局变量。
5%的方法s4main7TeacherCACycfC
// Teacher.__allocating_init()
sil hidden [exact_self_class] [ossa] @$s4main7TeacherCACycfC : $@convention(method) (@thick Teacher.Type) -> @owned Teacher {
bb0(%0 : $@thick Teacher.Type):
%1 = alloc_ref $Teacher // user: %3 //堆上分配内存空间
// function_ref Teacher.init()
%2 = function_ref @$s4main7TeacherCACycfc : $@convention(method) (@owned Teacher) -> @owned Teacher // user: %3
%3 = apply %2(%1) : $@convention(method) (@owned Teacher) -> @owned Teacher // user: %4
return %3 : $Teacher // id: %4
} // end sil function '$s4main7TeacherCACycfC'
我们在这里看到了__allocating_init
方法,在xcode中添加条件断点。
0x100001c04 <+20>: callq 0x100001d58 ; symbol stub for: swift_allocObject
0x100001c09 <+25>: movq %rax, %r13
0x100001c0c <+28>: callq 0x100001c40 ; swiftTest.Teacher.init() -> swiftTest.Teacher at main.swift:11
__allocating_init里调用了swift_allocObject
我们去swift源码中找到 _swift_allocObject
方法
开辟空间,根据
metadata
创建HeapObject
对象
InlineRefCounts refCounts
/// The Swift heap-object header.
/// This must match RefCountedStructTy in IRGen.
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *metadata;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
#ifndef __swift__
HeapObject() = default;
// Initialize a HeapObject header as appropriate for a newly-allocated object.
constexpr HeapObject(HeapMetadata const *newMetadata)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Initialized) //引用计数
{ }
Swift对象的内存结构HeapObject,有两个属性: ⼀个是Metadata,⼀个是RefCount,默认占⽤16字节⼤⼩
Int 8字节
print(MemoryLayout<String>.stride)
·····················
16
我们可以看到String占用的内存时16
所以最后占用了40字节大小。
类结构探索
using HeapMetadata = TargetHeapMetadata<InProcess>;
HeapMetadata
其实是TargetHeapMetadata
,而TargetHeapMetadata
里面并没有属性,找到他的父类TargetMetadata
。
private:
/// The kind. Only valid for non-class metadata; getKind() must be used to get
/// the kind value.
StoredPointer Kind;
只有一个属性StoredPointer
的kind
,这里记录这什么类型的元数据。
从方法里去寻找
/// Get the class object for this type if it has one, or return null if the
/// type is not a class (or not a class with a class object).
const TargetClassMetadata<Runtime> *getClassObject() const;
继承链为TargetClassMetadata
-->TargetAnyClassMetadata
-->TargetHeapMetadata
-->TargetMetadata
经过源码的阅读,我们应该能得出当前 metadata 的数据结构体了
struct swift_class_t: NSObject{
void *kind; //isa, kind(unsigned long)
void *superClass;
void *cacheData
void *data
uint32_t flags; //4
uint32_t instanceAddressOffset; //4
uint32_t instanceSize;//4
uint16_t instanceAlignMask; //2
uint16_t reserved; //2 11
uint32_t classSize; //4
uint32_t classAddressOffset; //4
void *description;
// ...
};
网友评论