Swift-进阶 01:Swift源码编译

Swift-进阶 01:Swift源码编译,第1张

准备工作

第一步:clone swift 源码

swift源码版本需要与 Xcode 版本匹如迟配(Xcode 12.2对应手兄 swift-5.3.1-Release ) swift源码地址

第二步:update-checkout

这渣薯李步主要是 clone 编译swift 相关的库

第三步:采用ninja编译

第四步:使用VSCode调试Swift

Swift进阶-类与结构体

Swift-函数腔让丛派发

Swift进阶-属性

Swift进阶-指针

Swift进阶-内存管理

Swift进阶-TargetClassMetadata和TargetStructMetadata数据结构源码分析

Swift进阶-Mirror解析

Swift进阶-闭包

Swift进阶-协议

Swift进阶-泛型

Swift进阶-String源码解析

Swift进阶-Array源码解析

创建一个空的字符串发生了什么?

这里并不能看出String的内存结构。那么接下来就借助 Swift源码 的方式看看String在内存中到底是如何存储的。

打开swift源码 ->stdib里的 String.swift

最直观地可以看到 String 是一个结构体,就是我们所说的值类型;它有一个成员变量 _StringGuts

其中最后有一个创建空字符串初始化方式 self.init(_StringGuts()) :

接下来看看这个 _StringGuts 到底是什么东西?

同样找到swift源码 ->stdib里的 StringGuts.swift

_StringGuts 也是一个结构体,它有一个成员变量是 _StringObject 类型的实例;

并且在最后是通过初始化出一个 _StringObject 类型的实例来初始化 _StringGuts 的。

所以真正swift的 String 的实质就是 _StringObject 。接下来看看 _StringObject 到底是什么玩意儿?

找到swift源码 ->stdib里的 StringObject.swift ,可以看到 _StringObject 是一个结构体,再找到空字符串的初始化函数:

ps: 注意这里初始化时的传参,下面会说到这几个成员

最终找到字符串最终初始化函数,该函数是对成员的初始化赋值,那么只要搞懂这几个成员是代表什么意思,那就能搞清楚字符串的底层实质了伍樱。

_StringObject 存储着一些成员变量,文章最开始使用x/8g格式化输出一个空字符串对象empty的时候,那我猜测:输出的内容应该就是 _StringObject 里的_count、_variant、_discriminator、_flags。

internal var _variant: Variant 是一个枚举值,默认是immortal 0:

internal var _discriminator: UInt8 在初始化的时候传递了一个Nibbles.emptyString( Nibbles 是一个枚举类型):

0xE000_0000_0000_0000 与文章最上面截图相对应起来了:

那接下来我们就能测试一下字符串了:

字符a的ASCII编码是97,97的16进制是61,注意那个2的字节位的输出

小于等滑缺于15个字符串时,会记录字符串的位数。

对于小字符串(小于等于15个字符串)来说,是优先直接存到内存当中,无需另外分配内存空间的。(和NSString差不多类似)

接下来看看中文字符

中文字符不是ASCII编码,一个中文字符占据3个字节(24位),也是我们上面通过源码分析得出的使用了 0xA000_0000_0000_0000

所以 _StringObject.Nibbles 是一个识别器,去识别字符串是不是ASCII编码。

对于大字符串(大于15个字符串)来说,原本的小字符串占据的15个字节已经不足以存储字符串了,那就会发生改变:

来看看0x8000000000000000在源码中出现的定义是一个大原始字符串:

那剩下的 0x000000010000b860 到底是什么东西呢?它是字符串的内存 相对地址;

那应该偏移多少呢?来看源码里的注解

意思是0x10000b860需要加上偏移量 nativeBias 即32,32的16进制是0x20:

0x10000b860 + 0x20 = 0x10000b880

在源码注解里找到大字符串标志位

大字符串前8位就记录着这些标志位信息,0xd000000000000012就是大字符串前8位,拿到科学计算器里看看标志位:

所以count是0x12,转换成10进制就是18,正好对应18个字符。

对于 String 来说,它并不支持通过下标的方式获取字符

只能通过 String.Index 的方式来访问

对于 Swift 来说, String 是一系列字符的集合,也就意味着 String 中的每一个元素是不等长的。那也就意味着我们在进行内存移动的时候步长是不一样的,什么意思?

比如我们有一个 Array 的数组(Int 类型),当我们遍历数组中的元素的时候,因为每个元素的内存大小是一致的,所以每次的偏移量就是 8 个字节。

但是对于字符串来说不一样,比如我要方位 str[1] 那么我是不是要把我这个字段遍历完成之后才能够确定是的偏移量?

依次内推每一次都要重新遍历计算偏移量,这个时候无疑增加了很多的内存消耗。这就是为什么我们不能通过 Int 作为下标来去访问 String 。

可以很直观的看到 Index 的定义:

position aka encodedOffset 一个 48 bit 值,用来记录码位偏移量;

transcoded offset : 一个 2 bit 的值,用来记录字符使用的码位数量;

grapheme cache : 一个 6 bit 的值,用来记录下一个字符的边界;

reserved : 7 bit 的预留字段;

scalar aligned : 一个 1 bit 的值,用来记录标量是否已经对齐过。

String.Index 的本质就是一个64位的位域信息,这个位域信息展示的就是上面的解释。

创建 String.Index 实际上就是通过 encodedOffset 或者 transcoded offset , encodedOffset 就是方便我们从内存中通过下标访问到字符串。

This is a completed finance app with Swift 3 And Nodejs.

The purpose i write it is i'm try to be a fullstack developer.

The ScreenShot as below

GitHub

Swift 3 + Realm + RxSwift + Alamofire

The project use Carthage as packmanager, please execute 'carthage update' before run

The project use npm &&MongoDB, please make sure you install it all before run

email: funpig@hotmail.com

QQ: 550462 (pls comments: github)

CNode

MIT


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12224596.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-22
下一篇 2023-05-22

发表评论

登录后才能评论

评论列表(0条)

保存