3

iOS摸鱼周报 第四十期

 2 years ago
source link: https://mp.weixin.qq.com/s/y4229I_l8aLILR7WA7y01Q
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
640?wx_fmt=jpeg
  • 话题:启用新封面
  • Tips:Swift 中的预编译
  • 面试模块:dyld 2 和 dyld 3 的区别;编译流程
  • 优秀博客:包依赖管理工具
  • 学习资料:从头开发一个迷你 Go 语言编译器
  • 开发工具:git 资源库浏览工具 Tig

@zhangferry:2022 年第一期摸鱼周报,从本期开始我们会使用新的封面,新封面由设计师朋友 Polaris 设计。这个场景表达的主题就是摸鱼,工作中的摸鱼不代表我们不尽职,而是我们对自由生活的向往。既要努力工作也要 Work Life Blance,2022 年,加油!

开发Tips

整理编辑:zhangferry

Swift 中的预编译

Clang 中有预编译宏的概念,在 Xcode 中其对应的是 Build Setting -> Apple Clang - Preprocessing 中的 Preprocessor Macros。这里可以根据不同的 Configuration 设置不同的预编译宏命令,其中 Debug 环境下的 DEBUG=1 就是内置的宏命令,我们通常使用的以下写法就是对应的这个配置:

#if DEBUG
// debug action
#end

如果需要新增 Configuration,比如 Stage,我们想要一个新的预编译宏比如 STAGE 表示它,如果这么做:

640?wx_fmt=png

在 Objective-C 的代码中是可行的,对于 Swift 代码则无效。这是因为 Swift 使用的编译器是 swiftc,它无法识别 clang 里定义的预编译宏。

解决方案是利用 SWIFT_ACTIVE_COMPILATION_CONDITIONS 这个配置变量,它对应 Build Setting 里的 Active Compilation Conditions。做如下设置即可让 STAGE 宏供 Swift 代码使用:

640?wx_fmt=png

整理编辑:zhangferry

dyld 2 和 dyld 3 有哪些区别

dyld 是动态加载器,它主要用于动态库的链接和程序启动加载工作,它目前有两个主要版本:dyld 2 和 dyld 3。

dyld 2

dyld2[1] 从 iOS 3.1 开始引入,一直到 iOS 12 被 dyld 3 全面代替。它经过了很多次版本迭代,我们现在常见的特性比如 ASLR,Code Sign,Shared Cache 等技术,都是在 dyld 2 中引入的。dyld 2 的执行流程是这样的:

640?wx_fmt=png
  • 解析 mach-o 头文件,找到依赖库,依赖库又可能有别的依赖,这里会进行递归分析,直到获得所有 dylib 的完整图。这里数据庞大,需要进行大量的处理;
  • 映射所有 mach-o 文件,将它们放入地址空间;
  • 执行符号查找,若你的程序使用 printf 函数,将会查找 printf 是否在库系统中,然后我们找到它的地址,将它复制到你的程序中的函数指针上;
  • 进行 bind 和 rebase,修复内部和外部指针;
  • 运行一些初始化任务,像是加载 category、load 方法等;
  • 执行 main;

dyld 3

dyld 3 在 2017 年就被引入至 iOS 11,当时主要用来优化系统库。现在,在 iOS 13 中它也将用于启动第三方 APP,完全替代 dyld 2。

dyld 3 最大的特点就是引入了启动闭包,闭包里包含了启动所需要的缓存信息,而且这个闭包在进程外就完成了。在打开 APP 时,实际上已经有不少工作都完成了,这会使 dyld 的执行更快。

最重要的特性就是启动闭包,闭包里包含了启动所需要的缓存信息,从而提高启动速度。下图是 dyld 2 和 dyld 3 的执行步骤对比:

640?wx_fmt=png

dyld 3 的执行步骤分两大步,以图中虚线隔开,虚线以上进程外执行,以下进程创建时执行:

  • 前 3 步查找依赖和符号相对耗时,且涉及一些安全问题,所以将这些信息做成缓存闭包写入磁盘里,对应地址:tmp/com.apple.dyld。闭包会在重启手机/更新/下载 App 的首启等时机创建。

  • 进程启动时,读取闭包并验证闭包有效性。

  • 后面步骤同 dyld 2

iOS 13中dyld 3的改进和优化[2]

iOS dyld 前世今生[3]

一般的编译器架构,比如 LLVM 采用的都是三段式,也即从源码到机器码需要经过三个步骤:

前端 Frontend -> 优化器 Optimizer -> 后端 Backend

这么设计的好处就是将编译职责进行分离,当新增语言或者新增 CPU 架构时,只需修改前端和后端就行了。

其中前端受语言影响,Objective-C 和 Swift 对应的前端分别是 clang 和 swiftc。下图整理了两种语言的编译流程:

640?wx_fmt=png

编译前端做的工作主要是:

  1. 词法分析:将源码进行分割,生成一系列记号(token)。
  2. 语法分析:扫描上一步生成的记号生成语法树,该分析过程采用上下文无关的语法分析手段。
  3. 语义分析:语义分析分为静态语义分析和动态语义分析两种,编译期间确认的都是静态语义分析,动态语义需运行时期间才能确定。该步骤包括类型匹配和类型转换,会确认语法树中各表达式的类型。

之后导出 IR 中间件供优化器使用。这一步 Swift 会比 ObjC 多几个步骤,其中一个是 ClangImporter,这一步用于兼容 OC。它会导入 Clang Module,把 ObjC 或者 C 的 API 映射为 Swift API,导出结果能够被语义分析器使用。

另外一个不同是 Swift 会有几个 SIL 相关的步骤(蓝色标注),SIL 是 Swift Intermediate Language 的缩写,意为 Swift 中间语言,它不同于 IR,而是特定于 Swift 的中间语言,适合用于对 Swift 源码进行分析和优化。它这里又分三个步骤:

  1. 生成原始的 SIL
  2. 进行一些数据流诊断,转成标准 SIL
  3. 做一些特定于 Swift 的优化,包括 ARC、泛型等

编译前端会生成统一的 IR (Intermediate Representation) 文件传入到优化器,它是一种强类型的精简指令集,对目标指令进行了抽象。Xcode 中的 Optimization Level 的几个优化等级: -O0 , -O1 , -O2 , -O3 , -Os,即是这个步骤处理的。

如果开启了 Bitcode,还会转成 Bitcode 格式,它是 IR 的二进制形式。

这个步骤相对简单,会根据不同的 CPU 架构生成汇编和目标文件。

项目编译是以文件为单位的,跨文件调用方法是无法定位到调用地址的,链接的作用就是用于绑定这些符号。链接分为静态链接和动态链接两种:

  • 静态链接发生在编译期,在生成可执行程序之前会把各个 .o 文件和静态库进行一个链接。常用的静态链接器为 GNU 的 ld,LLVM4 里也有自己的链接器 lld

  • 动态链接发生在运行时,用于链接动态库,它会在启动时找到依赖的动态库然后进行符号决议和地址重定向。动态链接其为 dyld

Swift.org - Swift Compiler[4]

整理编辑:东坡肘子

1、iOS包依赖管理工具[5] -- 来自掘金:小小青叶

@东坡肘子:本系列一共六篇文章,不仅从原理、使用、创建自定义库等方面,对 CocoaPods 和 Swift Package Manager 进行了介绍,并且对两种包管理工具进行了比较。

2、CocoaPods Podspec 解析原理[6] -- 来自楚权的世界:楚权

@东坡肘子:在 CocoaPods 中,podspec 文件主要用于描述一个 pod 库的基本信息,包括:名称、版本、源、依赖等等。本文介绍了如何通过 DSL 方法将配置的属性保存在一个对象的哈希表中,通过构建一棵保存所有配置信息的树从而建立相互之间的依赖关系。

3、关于 Swift Package Manager 的一些经验分享[7] -- 来自:字节跳动技术团队

@东坡肘子:Swift Package Manager 是 Apple 为了弥补当前 iOS 开发中缺少官方组件库管理工具的产物。相较于其他组件管理控件,他的定义文件更加轻松易懂,使用起来也很 Magic,只需将源码放入对应的文件夹内,Xcode 就会自动生成工程文件,并生成编译目标产物所需要的相关配置。同时,SPM 与 Cocoapods 相互兼容,可以在特性上提供互补。本文除了介绍 Swift Package Manager 的现状、常见使用方法外,还阐述了作者对于 SPM 未来的一些思考。

4、解决swift package manager fetch慢的问题[8] -- 来自简书:chocoford

@东坡肘子:由于网络的某些限制,在 Xcode 中直接 fetch Github 上的 SPM 库并不容易。本文中给出了几种提高 fetch 成功率的解决方案。(编辑特别提示:Xcode 程序包中内置了终端、命令行工具等应用,任何在系统终端下的代理设定对其都不会产生作用。使用 SS + Proxifier 的方式可以实现让 Xcode 中的网络数据从指定代理通过)

5、Swift Package Manager 添加资源文件[9] -- 来自掘金:moxacist

@东坡肘子:从 swift-tool-version 5.3 版本开始,Swift Package Manager 提供了在包中添加资源文件的能力。本文是 WWDC 2020 —— 【Swift 软件包资源和本地化】 专题演讲的中文整理。

整理编辑:Mimosa

《µGo语言实现——从头开发一个迷你Go语言编译器》

地址:https://github.com/chai2010/ugo-compiler-book

µGo 是 Go 语言的真子集(不含标准库部分), 可以直接作为 Go 代码编译执行,作者尝试以实现 µGo 编译器为线索,以边学习边完善的自举方式实现一个玩具语言,目前还没写完,对编译器或者 Go 感兴趣的小伙伴可以关注一下。这里有一份作者写的 Go 编译器定制简介[10] 供参考,同时作者还有《Go语法树入门(出版名:Go语言定制指南)》和《Go语言高级编程》等开源图书作品。

整理编辑:CoderStar

地址:https://jonas.github.io/tig/

软件状态:开源、免费

软件介绍

Tig 是一个 git 资源库浏览器,采用 ncurses 开发,很适合习惯使用命令行进行 git 操作的小伙伴们。

640?wx_fmt=png
Tig

iOS 摸鱼周报,主要分享开发过程中遇到的经验教训、优质的博客、高质量的学习资料、实用的开发工具等。周报仓库在这里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的内容推荐可以通过 issue 的方式进行提交。另外也可以申请成为我们的常驻编辑,一起维护这份周报。另可关注公众号:iOS成长之路,后台点击进群交流,联系我们,获取更多内容。

iOS摸鱼周报 第三十九期

iOS摸鱼周报 第三十八期

iOS摸鱼周报 第三十七期

iOS摸鱼周报 第三十六期

[1]

dyld开源地址: https://github.com/opensource-apple/dyld/tree/master/src

[2]

iOS 13中dyld 3的改进和优化: https://easeapi.com/blog/blog/83-ios13-dyld3.html

[3]

iOS dyld 前世今生: https://www.yotrolz.com/posts/c2aae680/

[4]

Swift.org - Swift Compiler: https://www.swift.org/swift-compiler/#compiler-architecture

[5]

iOS包依赖管理工具: https://juejin.cn/post/6932739864613879821

[6]

CocoaPods Podspec 解析原理: http://chuquan.me/2022/01/03/podspec-analyze-principle/

[7]

关于 Swift Package Manager 的一些经验分享: https://juejin.cn/post/7007987863954391054

[8]

解决swift package manager fetch慢的问题: https://juejin.cn/post/7007987863954391054

[9]

Swift Package Manager 添加资源文件: https://juejin.cn/post/6854573220784242702

[10]

Go 编译器定制简介: https://chai2010.cn/ugo-compiler-book/talks/go-compiler-intro.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK