窥探Swift源码下的Array

本文深入探讨Swift的Array,从Array的定义、用途到其底层实现,特别是关注其写时复制(Copy-on-Write)机制。通过分析源码,解释了在数组遍历和修改过程中如何通过引用计数判断是否开辟新空间,从而实现高效的空间利用。同时,文章提供了一步步的调试过程,帮助理解Array的内存管理和存储结构。
摘要由CSDN通过智能技术生成

db2edc7d0b58b4e87588b56e4882f96b.jpeg

 11582c69c97f6a69aa9c55192be5e8d4.gif 

本文字数: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是什么

ArraySwift下数组的实现,了解Swfit的都知道,Swift下的大多数的对象均是由struct组成的,我们找到源码中的Array的定义

// File: Array.swift, line: 299

@frozen public struct Array<Element> : Swift._DestructorSafeContainer {
  
  // ...
}

所以ArraySwift下本质就是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")处打断点

d6eb7edafac3693e02641e522ae982c7.jpeg

x/8gLLDB(Low Level Debugger)下的调试命令,作用是查看内存地址里的值

从图中可以看到,并没有找到1,2,3的信息,内存里面只有0x0000000101046c70,猜测是内存某块区域上的地址,所以现在的疑问也有了

  • Array保存的地址是什么?

  • Array保存的数据去哪了?

  • Array的写复制如何实现的?

带着这三个疑问我们继续往下探索...

生成ArraySIL文件

首先我刚才代码文件修改为如下所示,最简单的初始化,有利于我们阅读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>) -> &#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值