问题: Swift中的字符串在内存中是如何存储的?
- 首要咱们来代码验证一下最简略的字符串–“空字符串”的内存散布
var empty = ""
print("empty \(empty)")
print(withUnsafePointer(to: &empty, { $0 }))
打印成果:
1. 检查Swift.String
源码链接, 查找”empty”
- 看关键代码发现底部的
public init
办法调用了内部的init
办法, 该初始化办法接收了一个_StringGuts
的对象作为入参. - 源码
357
行开始, 也可看到结构体String
持有_StringGuts
作为成员变量. 咱们继续深挖StringGuts
.
2. 检查StringGuts.swift
源码链接, 查找”empty”.
- 从
_StringGuts
结构体的初始化办法中, 能够看到该结构体持有StringObject
作为成员变量, 拨云见日, 深挖”StringObject”.
3. 检查StringObject
源码链接, 查找”empty”.
-
在
StringObject
的init(count: variant: discirminator: flags:)
办法中能够看到StringObject
结构体的4个成员变量. -
在创立一个字符串的过程中, 都存储了什么内容呢?
-
从上面的源码能够看到
String
结构体在底层存储的便是以上4个成员变量的内容. -
初始化办法中
discriminator
成员变量对应的的Nibbles
又是什么呢? -
能够看到
Nibbles
也是一个枚举类型, 但是这儿仅仅界说, 原始界说是:
-
能够看到, 这儿调用的办法判断是如果当时是ASCII码, 那么当时的Discriminator判别器便是
0xE000_0000_0000_0000
, 如果不是ASCII码便是0xA000_0000_0000_0000
-
检查一个空字符串的内存散布, 打印成果如下:
-
检查一个包括中文的字符串, 打印成果如下:
-
⭐️综上, 咱们能够看出A, E在这儿是用来标识当时是否是ASCII码, 其中后面的数字是用来代表当时的多少个字符串的长度.
-
_discriminator
占据4位, 每一位的标识如下:
- 大字符串的规矩和
Nibbles
的布局结构如下:
- 关于原生的Swift字符串来说, 采取的是
tail-allocated
(尾递归)存储, 也便是在当时实例分配有超出其最后存储属性的额定空间, 额定的空间可用于直接在实例中存储任意数据, 无需额定的堆分配.代码验证如下:
- 接下来咱们需求重视的是
0x8000000100003f60
这个值, 根据上面的源码剖析阅读, 咱们知道当时0x8
标识的是大字符串
, 这点咱们在源码里也能够找到答案: - 同时结合
nibbles
在内存中的布局咱们知道其中b60:b0
是存储字符串的地址, 当然这个地址要加上偏移量, 这个偏移量是32
, 这儿咱们可通过计算器来验证一下:
下面是ASCII码十六进制符号对照表:
- 那么前面的8个字节是什么呢? 咱们能够先从初始化的流程来剖析:
- 所以能够看到, 除了咱们当时的地址和标识位之外, 剩余的便是
countAndFlags
, 这儿咱们能够看到布局如下:
- 第一个标志位是
isASCII
, 如果咱们修改成中文, 这儿就会改变
- ⭐️综上, 咱们能够发现Small strings(长度小于等于15的小字符串)直接存在内存中, Large strings(长度大于15的大字符串)存储的是内存地址
回答: Small strings(长度小于等于15的小字符串)直接存在内存中, Large strings(长度大于15的大字符串)存储的是内存地址
发文不易, 喜爱点赞的人更有好运气 :), 定时更新+重视不走失~
ps:欢迎参加笔者18年树立的研究iOS审核及前沿技术的三千人扣群:662339934,坑位有限,备注“网友”可被群管通过~