本文字数:6730字
预计阅读时间:15 分钟
用最通俗的语言,描述最难懂的技术
前情提要
我在之前的文章一次遍历导致的崩溃
中提到了,如果有机会会把相关的Swift
集合源码阅读。
首先对自己的开发和知识储备会有进一步的升华,另外也可以洞察苹果是否有其他的🐮操作等,值得借鉴
前几天的时候也在自己的项目中发现了一些关于Array
的崩溃,日志如下
#0 Thread
NSGenericException
*** Collection <__NSArrayM: 0x2836a4120> was mutated while being enumerated.
...
很显然,一个可变数组在遍历的同时对数组进行了修改,因为Array
是线程不安全的,所以框架就警告我们不允许这么做,直接抛崩溃处理
问题很简单,但是我考虑的是目前的Apple
是如何实现的Array
,如果想知道之前的实现可以看《NSMutableArray原理揭露》
最近恰巧在学习和使用相关的Swift
的一些框架,趁着周末搬完砖,就开始了源码之旅,我们立刻出发
笔者的相关准备
编译好的
Swift 5.5
源码C++
基础
⚠️:以下源码都在顶部标注了文件以及出现的行数
Array是什么
Array
是Swift
下数组的实现,了解Swfit
的都知道,Swift
下的大多数的对象均是由struct
组成的,我们找到源码中的Array
的定义
// File: Array.swift, line: 299
@frozen public struct Array<Element> : Swift._DestructorSafeContainer {
// ...
}
所以Array
在Swift
下本质就是Struct
Array有什么用
有序的存储一组数据
Array的底层原理
新建一个项目
Xcode->File->New->Project->MacOS->Command Line Tool-Language(Swift) & Product Name
输入以下代码
var num: Array<Int> = [1, 2, 3]
withUnsafePointer(to: &num) {
print($0)
}
print("end")
并在print("end")
处打断点
x/8g
是LLDB(Low Level Debugger)
下的调试命令,作用是查看内存地址里的值
从图中可以看到,并没有找到1,2,3
的信息,内存里面只有0x0000000101046c70
,猜测是内存某块区域上的地址,所以现在的疑问也有了
Array
保存的地址是什么?Array
保存的数据去哪了?Array
的写复制如何实现的?
带着这三个疑问我们继续往下探索...
生成Array
的SIL
文件
首先我刚才代码文件修改为如下所示,最简单的初始化,有利于我们阅读SIL
文件
var num: Array<Int> = [1, 2, 3]
在终端使用命令swiftc -emit-sil main.swift | xcrun swift-demangle > ./main.sil
生成SIL
文件
// 全局标识以@开头,全局变量
// 局部标识以%开头,局部变量
// store写入内存
// load读取内存
// 具体语法请参考:https://llvm.org/docs/LangRef.html(搜索Intermediate Representation)
sil_stage canonical
// 系统内部导入的相关需要的动态库
import Builtin
import Swift
import SwiftShims
import Foundation
// 对一个存放Int可变数组进行setter和getter的声明
@_hasStorage @_hasInitialValue var num: Array<Int> { get set }
// num
sil_global hidden @main.num : [Swift.Int] : $Array<Int>
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
alloc_global @main.num : [Swift.Int] // id: %2,在堆上开辟一个空间
%3 = global_addr @main.num : [Swift.Int] : $*Array<Int> // user: %25,创建临时变量%3存放数组首地址
%4 = integer_literal $Builtin.Word, 3 // user: %6
// 初始化数组调用的入口方法,function_ref _allocateUninitializedArray<A>(_:)
%5 = function_ref @Swift._allocateUninitializedArray<A>(Builtin.Word) -> ([A], Builtin.RawPointer) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %6
%6 = apply %5<Int>(%4) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // users: %8, %7
%7 = tuple_extract %6 : $(Array<Int>, Builtin.RawPointer), 0 // user: %24
%8 = tuple_extract %6 : $(Array<Int>, Builtin.RawPointer), 1 // user: %9
%9 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*Int // users: %12
%10 = integer_literal $Builtin.Int64, 1 // user: %11,
%11 = struct $Int (%10 : $Builtin.Int64) // user: %12
store %11 to %9 : $*Int // id: %12
%13 = integer_literal $Builtin.Word, 1 // user: %14
%14 = index_addr %9 : $*Int, %13 : $Builtin.Word // user: %17
%15 = integer_literal $Builtin.Int64, 2 // user: %16
%16 = struct $Int (%15 : $Builtin.Int64) // user: %17
store %16 to %14 : $*Int // id: %17
%18 = integer_literal $Builtin.Word, 2 // user: %19
%19 = index_addr %9 : $*Int, %18 : $Builtin.Word // user: %22
%20 = integer_literal $Builtin.Int64, 3 // user: %21
%21 = struct $Int (%20 : $Builtin.Int64) // user: %22
store %21 to %19 : $*Int // id: %22
// function_ref _finalizeUninitializedArray<A>(_:)
%23 = function_ref @Swift._finalizeUninitializedArray<A>(__owned [A]) -> [A] : $@convention(thin) <τ_0_0> (@owned Array<τ_0_0>) -> &#