【Swift底层】Swift底层探索方法介绍

Swift底层探索方法介绍

在探索Swift一些底层原理的时候,常常会无从下手,分享几个可行的路子。

基础

  1. Swift源码定义了写swift程序的基本功能层,如基本的数据类型,通用的数据结构,全局函数,常用的抽象协议等。我们也可以通过研究标准库的代码来学习swift中各类数据结构和方法的底层实现。
  2. iOS开发的语言不管是OC还是Swift都是通过LLVM进行编译的。如下图所示:

swift的前端编译器为swiftc。一个Swift文件的编译从编译器的角度会经过如下步骤。

  1. Code经过词法分析、语法分析生成AST抽象语法树
  2. AST通过SILGen生成原生SIL文件(代码量很大,没有进行类型检查等)
  3. 原生SIL文件优化生成最终SIL文件
  4. 该SIL文件通过IRGen生成IR
  5. IR生成机器码

注:生成可执行文件的流程中,swiftc和clang的区别就在于swiftc多了生成SIL文件这一步。

方案说明

swiftc

在上述介绍的swift编译流程中,我们可以通过swiftc -h查看常用指令

perl 复制代码
MODES:
  -dump-ast              解析和类型检查源文件 & 转换成 AST
  -dump-parse            解析源文件 & 转换成 AST
  -emit-assembly         生成汇编文件
  -emit-bc               生成 LLVM Bitcode 文件
  -emit-executable       生成已链接的可执行文件
  -emit-imported-modules 生成已导入的库
  -emit-ir               生成 LLVM IR 文件
  -emit-library          生成已连接的库
  -emit-object           生成目标文件
  -emit-silgen           生成 raw SIL 文件(第一个阶段)
  -emit-sil              生成 canonical SIL 文件(第2个阶段)
  -index-file            为源文件生成索引数据
  -print-ast             解析和类型检查源文件 & 转换成更简约的格式更好的 AST
  -typecheck             解析和类型检查源文件
  ···

就可读性而言,我们常选择sil文件来进行分析。以下主要对sil文档的分析进行简单介绍。如果想查看更多信息,可以查看官方文档

IR基本语法

rust 复制代码
// 数组
[<elementnumber> x <elementtype>]
// example
 alloca [24 x i8], align 8 24个i8都是0

// 结构体
%swift.refcounted = type { %swift.type*, i64 }
//表示形式
%T = type {<type list>} //这种和C语⾔的结构体类似

// 指针类型
<type> *
//表示形式
i64* //64位的整形

// getelementptr指令:获取数组和结构体的成员
<result> = getelementptr <ty>, <ty>* <ptrval>{, [inrange] <ty> <id x>}* 
<result> = getelementptr inbounds <ty>, <ty>* <ptrval>{, [inrange] <ty> <idx>}*

sil文件常见指令

  • @main:入口【sil中的标识符名称以@为前缀】
  • %1%2:sil中的寄存器(常量),一旦赋值不可修改
  • alloc_global:分配一个全局变量
  • metatype:获取元类型
  • function_ref:获取函数指针
  • apply:调用方法
  • alloc_ref:分配一个类型为T的实例对象,默认的引用计数为1,也就是在堆上分配内存空间
  • alloc_stack:在栈区为变量分配内存空间
  • integer_literal:构建一个整形变量(整形变量在swift中其实为结构体类型。)
  • store {A} to {B}:将A存储到指定变量B中

注:在SIL文件中常常可以看到类似s4main6personAA11PandaPersonCvp这样经过混淆的变量名,可以通过xcrun swift-demangle s4main6personAA11PandaPersonCvp指令来将其还原。

寄存器

在sil文件中可以看到许多%0,%1,代表的是寄存器。请注意这里的寄存器和我们平时研究编译原理中的寄存器不太一样。 我们在工程中打开调试模式并开启Always Show Dissasembly

之后通过register read命令拿到的这些寄存器信息中的寄存器是不同的。如下图所示,这些寄存器是真实存在的。

https://oscimg.oschina.net/oscnet/up-edcf14ac116a4ae6600907bf16426f3cfb5.png

而我们的sil文件中的寄存器是虚拟的。我们可以理解为常量【一旦赋值无法改变】。运行的时候会对应到真实的寄存器。

Example

首先我们创建一个main.swift文件

ini 复制代码
class PandaPerson {
    var name: String = "Letty"
    var age: Int = 18
}

let person = PandaPerson()

我们现在要探索的就是let person = PandaPerson()这句代码背后做了什么事情。类比OC创建对象,alloc内存分配,init初始化操作。而Swift提供了这样默认的初始化器,我们将通过SIL文件来探索。

css 复制代码
# 执行下述命令生成sil文件
swiftc -emit-sil main.swift > main.sil

首先我们可以找到入口函数@main

swift 复制代码
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32

下面具体看看@main函数中在创建对象部分做了什么事

less 复制代码
  // 1. 分配一个全局变量person
  alloc_global @$s4main6personAA11PandaPersonCvp
  // 2. 为该变量分配内存空间(即创建实例对象)
  %3 = global_addr @$s4main6personAA11PandaPersonCvp : $*PandaPerson
  // 3. 获取PandaPerson元类型
  %4 = metatype $@thick PandaPerson.Type
  // 4. 获取PandaPerson的__allocating_init()函数指针
  %5 = function_ref @$s4main11PandaPersonCACycfC : $@convention(method) (@thick PandaPerson.Type) -> @owned PandaPerson
  // 5. 调用__allocating_init()方法,将方法返回值存至%6
  %6 = apply %5(%4) : $@convention(method) (@thick PandaPerson.Type) -> @owned PandaPerson
  // 6. 将创建的对象存储到第三步中为全局变量person分配的内存空间中
  store %6 to %3 : $*PandaPerson

  ···

swift源码

仓库地址: github.com/apple/swift

目录结构

less 复制代码
graph TD
A(swift-main) --> B("docs:文档")
A --> C("stdlib:swift源码")
A --> D("lib:c++源码")
A --> E("include:c++头文件")
A --> I("···")

常用索引

  • 标准库:./stdlib/public/core

    • map、filter:./stdlib/public/core/Sequence.swift
    • flatMap、compactMap、reduce:./stdlib/public/core/SequenceAlgorithms.swift
    • Substring:./stdlib/public/core/Substring.swift
    • Optional:./stdlib/public/core/Optional.swift
  • MetaData:./include/swift/ABI/MetaData.h

总结

SIL文件可以帮助我们探究方法调用流程和部分数据结构的底层实现,swift源码则更贴近开发者,帮助我们了解swift对于数据和方法等设计及其原理。具体问题具体分析,笔者建议两者可以结合使用。

相关推荐
若水无华1 天前
fiddler 配置ios手机代理调试
ios·智能手机·fiddler
Aress"1 天前
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
ios·uni-app·ipa安装
Jouzzy2 天前
【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
安全·ios·iphone
瓜子三百克2 天前
采用sherpa-onnx 实现 ios语音唤起的调研
macos·ios·cocoa
左钦杨2 天前
IOS CSS3 right transformX 动画卡顿 回弹
前端·ios·css3
努力成为包租婆2 天前
SDK does not contain ‘libarclite‘ at the path
ios
安和昂3 天前
【iOS】Tagged Pointer
macos·ios·cocoa
I烟雨云渊T3 天前
iOS 阅后即焚功能的实现
macos·ios·cocoa
struggle20253 天前
适用于 iOS 的 开源Ultralytics YOLO:应用程序和 Swift 软件包,用于在您自己的 iOS 应用程序中运行 YOLO
yolo·ios·开源·app·swift
Unlimitedz3 天前
iOS视频编码详细步骤(视频编码器,基于 VideoToolbox,支持硬件编码 H264/H265)
ios·音视频