阅读 612

初探Swift

前言:swift已经发展了好几年了,目前版本趋于稳定,iOS之后的语言将会从Objective-c逐渐切换到swift,抱着学习的态度来研究下swift语言。

从一小段代码切入

SIL分析

class Person{
    var age = 18
    var height = 180
    var name = "Kevin"
}

var t = Person()

复制代码

很简单的声明了一个Person类,并且得到一个实例化的Person对象,那么从SIL的角度来分析一下发生了什么,使用命令swiftc -emit-sil main.swift >> ./main.sil && open main.sil得到一个SIL文件,接下来看看这个文件里发生了什么。 -w1141 这里可以看到上面是我们生命的Person的类,下面有一个main就是我们main.swift的入口,alloc_global创建一个全局变量,那么后面这个字符串是经过编码的一段东西,这里使用命令xcrun swift-demangle s4main6PersonCACycfCs4main6PersonCACycfC解码得到s4main6PersonCACycfC ---> main.Person.__allocating_init() -> main.Person,可以清晰的看到这个就是Person__allocating_init函数,之后通过函数调用得到Person的实例对象。 大体的概括就是这样,接下来对每一步进行描述:

  • alloc_gobal创建一个名为tPerson类的全局变量,s4main1tAA6PersonCvp ----> s4main1tAA6PersonCvp ---> main.t : main.Person
  • global_addr拿到全局变量的地址给到3%
  • metatype拿到PersonMetaData赋值给4%
  • apply调用__allocating_init并将结果赋值给全局变量t

__allocating_init()的过程中会发生什么呢,回到main.swift文件,借助符号断点来看看会发生什么 -w1377

可以看到实际分配内存空间的函数叫做swift_allocObject,但是在之前SIL文件中并不能找到它的身影,接下来切换到swift源码中进行探索。

swift_allocObject

swift源码中开始调试,很快可以确认到_swift_allocObject_这个函数,并对其打上断点开始调试 -w1406 tips:

  • requiredSize是指需要分配的内存空间为48字节
  • requiredAlignmentMask是指以8字节进行内存对齐

同时对于分配的内存空间是否为48字节,同样可以进行验证,通过class_getInstanceSize(Person.self)得到结果48 在内部会调用swift_slowAlloc函数

swift_slowAlloc

-w753 malloc_zone_malloc会在堆空间分配一块size大小的内存空间 总结 以上我们就可以得到swift在内存分配的过程的流程: __allocating_init -----> swift_allocObject -----> _ swift_allocObject_ -----> swift_slowAlloc -----> malloc_zone_malloc

实例对象的本质

通过对swift_allocObject的分析在分配到内存空间后,此函数的返回类型为HeapObject,这里是通过元数据metaData来初始化HeapObject

HeapObject

constexpr HeapObject(HeapMetadata const *newMetadata) 
    : metadata(newMetadata)
    , refCounts(InlineRefCounts::Initialized)
  { }
  
复制代码

这里是HeapObject的初始化函数,这里包含两个数据

  • metadata:元数据
  • refCounts:引用计数,同样是通过ARC来管理内存

-w793

metadata是一个指针类型,占用8字节空间 -w758 通过源码可以看到refCounts是一个类的指针类型,故也占8字节空间

结论 HeapObject默认占用内存空间为metadata类型指针8字节 + refCounts8字节 总计16字节 所以对于开篇引入的例子Person对象占用的内存空间为:8+8+8+8+16 合计48字节

类结构

刚刚分析了实例对象,在HeapObject存在metadata元数据,它记录的就是类的信息。 -w772

跟进源码中发现metadata不过是一个别名,其真正是TargetHeapMetadata -w802 TargetHeapMetadata是一个模板类,在初始化方法中看到有一个参数kind应该是来自于父类,切换至TargetMetadata-w808

-w657

-w648

kind是用来区分当前是何种类型的元数据,区分文件是名为MetadataKind.def,所有的元数据类型都在此文件中。 kind代表的所有类型的元数据整理如下

namevalue
Class0x0
Struct0x20
Enum0x201
Optional0x202
ForeignClass0x203
Opaque0x300
Tuple0x301
Function0x302
Existential0x303
Metatype0x304
ObjcClassWrapper0x305
ExistentialMetatype0x306
HeapLocalVariable0x400
HeapGenericLocalVariable0x500
ErrorObject0x501
LastEnumerated0x7ff

我们当前代码中Person是一个类,那么找到了一个针对于类的方法 -w813 可以看到在判断类型过后发现当前元数据是类的类型时,直接做了强转,此时跳转到ClassMetadata-w823 在其中看到很多属性,再到ClassMetadata的父类TargetAnyClassMetadata-w824

最后发现父类是TargetHeapMetadata它只有一个属性kind

-w1002

结论 当前类的结构整理后如下:

struct swift_class_t {
void * kind;
void * superClass;
void * cacheData;
void * data;
uint32_t flags; 
uint32_t instanceAddressOffset; 
uint32_t instanceSize;
uint16_t instanceAlignMask;
uint16_t reserved;
uint32_t classSize;
uint32_t classAddressOffset;
void *description;
}
复制代码

如果该类与Objective-C交互,则此时kind相当于isa

文章分类
iOS
文章标签