问题: Swift中的字符串在内存中是如何存储的?

  • 首要咱们来代码验证一下最简略的字符串–“空字符串”的内存散布
var empty = ""
print("empty \(empty)")
print(withUnsafePointer(to: &empty, { $0 }))

打印成果:

探究Swift的String底层实现

1. 检查Swift.String源码链接, 查找”empty”

探究Swift的String底层实现

  • 看关键代码发现底部的public init办法调用了内部的init办法, 该初始化办法接收了一个_StringGuts的对象作为入参.
  • 源码357行开始, 也可看到结构体String持有_StringGuts作为成员变量. 咱们继续深挖StringGuts.

2. 检查StringGuts.swift源码链接, 查找”empty”.

  • _StringGuts结构体的初始化办法中, 能够看到该结构体持有StringObject作为成员变量, 拨云见日, 深挖”StringObject”.
    探究Swift的String底层实现

3. 检查StringObject源码链接, 查找”empty”.

  • StringObjectinit(count: variant: discirminator: flags:)办法中能够看到StringObject结构体的4个成员变量.

    探究Swift的String底层实现

  • 在创立一个字符串的过程中, 都存储了什么内容呢?

    探究Swift的String底层实现

  • 从上面的源码能够看到String结构体在底层存储的便是以上4个成员变量的内容.

  • 初始化办法中discriminator成员变量对应的的Nibbles又是什么呢?

    探究Swift的String底层实现

  • 能够看到Nibbles也是一个枚举类型, 但是这儿仅仅界说, 原始界说是:

探究Swift的String底层实现

  • 能够看到, 这儿调用的办法判断是如果当时是ASCII码, 那么当时的Discriminator判别器便是0xE000_0000_0000_0000, 如果不是ASCII码便是0xA000_0000_0000_0000

    探究Swift的String底层实现

  • 检查一个空字符串的内存散布, 打印成果如下:

    探究Swift的String底层实现

  • 检查一个包括中文的字符串, 打印成果如下:

    探究Swift的String底层实现

  • ⭐️综上, 咱们能够看出A, E在这儿是用来标识当时是否是ASCII码, 其中后面的数字是用来代表当时的多少个字符串的长度.

  • _discriminator占据4位, 每一位的标识如下:

    探究Swift的String底层实现

探究Swift的String底层实现

  • 大字符串的规矩和Nibbles的布局结构如下:
    探究Swift的String底层实现

探究Swift的String底层实现

  • 关于原生的Swift字符串来说, 采取的是tail-allocated(尾递归)存储, 也便是在当时实例分配有超出其最后存储属性的额定空间, 额定的空间可用于直接在实例中存储任意数据, 无需额定的堆分配.代码验证如下:

探究Swift的String底层实现

  • 接下来咱们需求重视的是0x8000000100003f60这个值, 根据上面的源码剖析阅读, 咱们知道当时0x8标识的是大字符串, 这点咱们在源码里也能够找到答案:
    探究Swift的String底层实现
  • 同时结合nibbles在内存中的布局咱们知道其中b60:b0是存储字符串的地址, 当然这个地址要加上偏移量, 这个偏移量是32, 这儿咱们可通过计算器来验证一下:

探究Swift的String底层实现

下面是ASCII码十六进制符号对照表:

探究Swift的String底层实现

  • 那么前面的8个字节是什么呢? 咱们能够先从初始化的流程来剖析:

探究Swift的String底层实现

探究Swift的String底层实现

探究Swift的String底层实现

  • 所以能够看到, 除了咱们当时的地址和标识位之外, 剩余的便是countAndFlags, 这儿咱们能够看到布局如下:

探究Swift的String底层实现

探究Swift的String底层实现

  • 第一个标志位是isASCII, 如果咱们修改成中文, 这儿就会改变

探究Swift的String底层实现

  • ⭐️综上, 咱们能够发现Small strings(长度小于等于15的小字符串)直接存在内存中, Large strings(长度大于15的大字符串)存储的是内存地址

回答: Small strings(长度小于等于15的小字符串)直接存在内存中, Large strings(长度大于15的大字符串)存储的是内存地址

发文不易, 喜爱点赞的人更有好运气 :), 定时更新+重视不走失~

ps:欢迎参加笔者18年树立的研究iOS审核及前沿技术的三千人扣群:662339934,坑位有限,备注“网友”可被群管通过~