原創(chuàng):知識點總結(jié)性文章
創(chuàng)作不易粥航,請珍惜,之后會持續(xù)更新冤狡,不斷完善
個人比較喜歡做筆記和寫總結(jié)孙蒙,畢竟好記性不如爛筆頭哈哈项棠,這些文章記錄了我的IOS成長歷程,希望能與大家一起進步
溫馨提示:由于簡書不支持目錄跳轉(zhuǎn)马篮,大家可通過command + F 輸入目錄標題后迅速尋找到你所需要的內(nèi)容
目錄
- 一沾乘、LLVM
- 二、編譯流程
- 三浑测、 Package Manager
一翅阵、LLVM
在深入 SIL
之前,先簡單介紹 LLVM
迁央,經(jīng)典的 LLVM
三段式架構(gòu)如下圖所示掷匠,分為前端(Frontend
),優(yōu)化器(Optimizer
)和后端(Backend
)岖圈。當需要支持新語言時只需實現(xiàn)前端部分讹语,需要支持新的架構(gòu)只需實現(xiàn)后端部分,而前后端的連接樞紐就是 IR(Intermediate Representation
)蜂科,IR 獨立于編程語言和機器架構(gòu)顽决,故 IR 階段的優(yōu)化可以做到抽象而通用。
Frontend
前端經(jīng)過詞法分析(Lexical Analysis
)导匣,語法分析(Syntactic Analysis
)生成 AST才菠,語義分析(Semantic Analysis
),中間代碼生成(Intermediate Code Generation
)等步驟贡定,生成 IR赋访。
IR
IR 是 LLVM 前后端的橋接語言,其主要有三種格式缓待,這三種格式完全等價:
- 可讀的格式蚓耽,以
.ll
結(jié)尾 -
Bitcode
格式,以.bc
結(jié)尾 - 運行時在內(nèi)存中的格式
Optimizer
IR經(jīng)過優(yōu)化器進行優(yōu)化旋炒,優(yōu)化器會調(diào)用執(zhí)行各類 Pass
步悠。所謂 Pass
,就是遍歷一遍 IR瘫镇,在進行針對性的處理的代碼贤徒。LLVM 內(nèi)置了若干 Pass
,開發(fā)者也可自定義 Pass
實現(xiàn)特定功能汇四,比如插樁統(tǒng)計函數(shù)運行耗時等。
Xcode Optimization Level
在Xcode - Build Setting - Apple Clang - Code Generation - Optimization Level
中踢涌,可以選定優(yōu)化級別通孽,-O0
表示無優(yōu)化,即不調(diào)用任何優(yōu)化 Pass
睁壁。其他優(yōu)化級別則調(diào)用執(zhí)行對應(yīng)的 Pass
背苦。
Backend
后端將 IR 轉(zhuǎn)成生成相應(yīng) CPU 架構(gòu)的機器碼互捌。
二、Swift 編譯流程
Swiftc
不同于 OC 使用 clang
作為編譯器前端行剂,Swift 自定義了編譯器前端 swiftc
秕噪,如下圖所示。這里就體現(xiàn)出來 LLVM 三段式的好處了厚宰,支持新語言只需實現(xiàn)編譯器前端即可腌巾。對比clang
,Swift 新增了對 SIL(Swift Intermediate Language)
的處理過程铲觉。SIL 是 Swift 引入的新的高級中間語言澈蝙,用以實現(xiàn)更高級別的優(yōu)化。
Swift 編譯流程
Swift 源碼經(jīng)過詞法分析撵幽,語法分析和語義分析生成 AST灯荧。SILGen
獲取 AST 后生成 SIL,此時的 SIL 稱為 Raw SIL
盐杂。在經(jīng)過分析和優(yōu)化逗载,生成 Canonical SIL
。最后链烈,IRGen
再將 Canonical SIL
轉(zhuǎn)化為 LLVM IR
交給優(yōu)化器和后端處理厉斟。
三、 Package Manager
前言
Swift Package Manager
是 Apple 為了彌補當前 iOS 開發(fā)中缺少官方組件庫管理工具的產(chǎn)物测垛。相較于其他組件管理控件捏膨,他的定義文件更加輕松易懂,使用起來也很 Magic
食侮,只需將源碼放入對應(yīng)的文件夾內(nèi)号涯,Xcode 就會自動生成工程文件,并生成編譯目標產(chǎn)物所需要的相關(guān)配置锯七。同時链快,SPM
與 Cocoapods 相互兼容,可以在特性上提供互補眉尸。這篇文章將主要介紹該組件管理器的現(xiàn)狀域蜗,常見使用方法,和將來的一些思考噪猾。
本人是十分喜歡Swift Package
的霉祸,本地集成方便快捷,也給我很大的權(quán)力讓我所想落實到幾乎不可能落實的上游倉庫袱蜡。配合 Swift Access Control
丝蹭,例如 module
內(nèi)可訪問的 internal
屬性,很大程度上解決了寫 App 后臺的時候被 UI 意外調(diào)用造成的 crash坪蚁,彌補上 Swift 沒有class-private
訪問控制關(guān)鍵字的遺憾奔穿。調(diào)試可以直接打到代碼上镜沽,速度也很快。如果能為 Package
提供 .patch
的擴展文件贱田,再配合優(yōu)化后的遠端倉庫缅茉,這將很有可能取代臃腫的 Cocoapod。pod
會修改編譯目標的 xcconfig
男摧,而 Swift Package
通過提供library
和 workspace
的集成方式蔬墩,侵入性非常低。最后彩倚,Swift Package
的多平臺編譯的能力也非常好筹我,UIKit 一次編寫即可適配 iOS/iPadOS/tvOS/watchOS,編譯配置 CI
只需要調(diào)用 xcodebuild
即可自動解析帆离,如有缺失自動拉取蔬蕊,省時省力。個人項目我可能不會再碰 Cocoapods哥谷。
開源組件使用情況
查看當前開源組件的 SPM
接入供應(yīng)情況岸夯,不難發(fā)現(xiàn)幾乎全部還在維護的框架都支持使用這種方式集成。大到微軟的 APM SDK们妥,小到界面 UI 組件猜扮,均有良好的兼容支持。下面列舉一些可能會在后續(xù)開發(fā)中用到的組件监婶。資源選自 https://github.com/ivanvorobei/awesome-ios旅赢。檢測規(guī)則為是否在倉庫主目錄下存在 Package.swift
文件。統(tǒng)計中惑惶,56%左右的框架已經(jīng)適配了 SPM
接入煮盼,且已經(jīng)開始出現(xiàn)如 MarkdownUI
等框架僅適配 SPM
的情況。
優(yōu)勢
- 簡化的定義流程:將文件放入約定的目錄內(nèi)即可一鍵打包带污。
- 簡化的 SPM 版本管理:Xcode 會根據(jù)定義文件首行說明自動查找兼容的解決方案僵控。
- 簡化的上手流程:不需要安裝工具,也不需要命令行安裝組件鱼冀。
-
良好的持續(xù)集成能力:在完成項目配置以后报破,
xcodebuild
無縫銜接,自動拉倉千绪。 - 良好的兼容性:可與現(xiàn)有的大多數(shù)組件管理方案混用充易。
- 良好的調(diào)試能力:斷點快狠準。
缺點
- 文檔難找荸型。
- 使用遠端倉庫對網(wǎng)絡(luò)要求非常高蔽氨。
創(chuàng)建組件
創(chuàng)建組件可以在 Xcode 中選擇Swift Package
,也可以在命令行中寫入 swift package init
。命令行創(chuàng)建會將當前目錄名稱用作包名鹉究。
Creating library package: Desktop
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/Desktop/Desktop.swift
Creating Tests/
Creating Tests/DesktopTests/
Creating Tests/DesktopTests/DesktopTests.swift
定義組件
// swift-tools-version:5.5
import PackageDescription
let package = Package(
name: "MyLibrary",
products: [
.library(name: "MyLibrary", targets: ["MyLibrary"]),
],
dependencies: [ ],
targets: [
.target(name: "MyLibrary", dependencies: []),
.testTarget(name: "MyLibraryTests", dependencies: ["MyLibrary"]),
]
)
請勿忽略swift-tools-version:5.5
行,當打包編譯出現(xiàn)工具鏈版本不匹配踪宠、 SDK 版本自赔、系統(tǒng) API 最低版本等問題時需要首先到這里排查可能存在的問題。
Targets
先看 targets
柳琢,定義是 A target can define a module or a test suite
. 翻譯來說绍妨,就是一個 target
對應(yīng)一個 clang module
或者 一個測試目標。一個 target
內(nèi)只允許使用一類語言柬脸,比方說 Swift 或者 Objective-C/C/CPP他去。此處的name
只對當前 package
可見,可以填寫在任意一個 dependencies
內(nèi)倒堕。Target
支持 binary Target
灾测,可使用 XCFramwork
或 .a .so
等二進制。
Products
再看 products
垦巴,定義是 Products define the executables and libraries a package produces, and make them visible to other packages.
一個 product
可以包含多個 target
媳搪,他們會被編譯成產(chǎn)物提供給項目。如果其他項目依賴當前的 Swift Package
骤宣,此處的 name
可以填寫入其他Package
的依賴需求內(nèi)秦爆,一般對內(nèi)不可用。最后來看一下product
的幾種類型憔披。一般來說等限,常見的 .library
可 type
包含 .static
(默認) 和 .dynamic
。除開.library
還有 .executable
可選芬膝,用于編譯測試用二進制和 macOS 命令行工具望门。
.library(name: "MorphingLabel", targets: ["MorphingLabel"]),
.library(name: "MorphingLabelDynamic", type: .dynamic, targets: ["MorphingLabel"]),
.executable(name: "appdecrypt", targets: ["appdecrypt"])
資源文件
Swift Package
需要對每一個文件指明用途。代碼文件會自動識別并編譯打包蔗候,資源文件需要指定和說明怒允。Swift Package
會為每一個 Package
生成一個 module
擴展,以便直接調(diào)用锈遥。使用命令行將項目文件 Package.swift
轉(zhuǎn)換成 xcproj
則不會生成該模版定義文件纫事。以下定義會在 Bundle
類內(nèi)生成.module
屬性專門用于獲取 Particles
文件夾內(nèi)的資源。
.target(
name: "MorphingLabel",
exclude: ["Info.plist", "tvOS-Info.plist"],
resources: [ .process("Particles") ] // <-- 資源文件
),
目錄結(jié)構(gòu)
-
Swift Package
推薦使用原生目錄結(jié)構(gòu)所灸,不推薦自定義Path
丽惶。 -
Swift Package
導(dǎo)出頭文件有規(guī)定的位置,在當前Source Path
內(nèi)創(chuàng)建include
會自動導(dǎo)出爬立。 -
Swift Package
需要對每一個資源文件/文件夾顯示聲明钾唬,對通配符的適配存在 Bug。 - 當需要特定的文件目錄組織的時候可以使用 符號連接 來鏈接目標文件。
總體來說Swift Package
中一個Target
對應(yīng)一個 name
抡秆,而 項目根目錄/Sources/name
會作為當前Target
的工作搜索路徑奕巍。
├── Package.swift # 定義文件
├── README.md # 可忽略
├── Sources # 此文件夾內(nèi)全部文件都需要定義 不然會報錯
│ └── demo # target demo 的默認目錄
│ ├── Particles. # 在 target 內(nèi)聲明為資源文件
│ │ └── fire.png # 會自動打包成 bundle 拷貝并傳遞
│ ├── demo.swift # target demo 的項目源代碼
│ └── include # 導(dǎo)出頭文件
│ └── export.h # 頭文件
# 在 Sources / target name 厘頭的資源文件可在 Package.swift 內(nèi)定義。
# 需要在 target 內(nèi)添加 resources: [ .process("Particles") ]
XCFramework
關(guān)于編譯產(chǎn)物儒士,基礎(chǔ)的 Swift Package
可以生成靜態(tài)庫的止、動態(tài)庫,在這以后可以手動打包成 XCFramework
着撩。SPM
的打包工作流對XCFramework
非常友好诅福,可以參考下面這個腳本。
xcodebuild -create-xcframework \
-framework "$BUILD_FOLDER/iOS.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \
-framework "$BUILD_FOLDER/tvOS.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \
-framework "$BUILD_FOLDER/Simulator.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \
-framework "$BUILD_FOLDER/tvOSSimulator.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \
-output Build/LTMorphingLabel.xcframework
目前有針對 Package.swift
生成并編譯 XCFrameowrk
的懶人工具拖叙,但是由于其依賴將項目轉(zhuǎn)換成 xcproj
的編譯方法氓润,攜帶資源文件的 Swift Package
并不能用。
實踐
目前筆者有一個開源的私人項目使用了 SPM
薯鳍,可以拉下倉庫來看一看圾旨。Xcode 在解析各種依賴方面并不穩(wěn)定盔然,所以項目采用的方案是將所有代碼拉到本地并通過修改dependencies
的方式采用本地解析集成齐板。本地集成的方式非常穩(wěn)定景鼠,而且最大程度的保證了你修改源碼的能力。Swift 發(fā)展非澈迹快悯舟,目前不推薦url
直接集成遠端倉庫。
https://github.com/SailyTeam/Saily 在本地創(chuàng)建 xcworkspace
以后便可以直接將 Package.swift
中的product
添加到項目的編譯流程內(nèi)砸民。這里再次贊賞Swift Package
的多元兼容抵怎,其中有一些庫是純 Objective-C 撰寫的,可以一鍵無縫集成岭参。
本地集成的其他好處自然也包含 0 編譯警告反惕,遇到任何問題你都可以直接打斷點到 Swift Package
的代碼上。而 Cocoapod 經(jīng)常不靈演侯。關(guān)于編譯警告姿染,養(yǎng)眼準備!
其中可以重點關(guān)注幾個混合編譯的庫的定義和 Fluent Icon
庫的定義文件秒际。其中就如上面描述的一樣悬赏,include
文件會被自動導(dǎo)出給 Swift 使用。
常見問題
Q: 我導(dǎo)入了 Swift Package
到項目娄徊,但無法import
A: 請command + shift + K
清理項目重新編譯闽颇。Swift Package
有 module
緩存。
Q: 我的include
指向上級目錄的頭文件寄锐,導(dǎo)出失敗了
A: 請清理項目重新編譯兵多,有時需要重啟 Xcode尖啡。
Q: 我在編譯的時候指定了最低要求 iOS 13,為何 Swift Package
無法調(diào)用 API剩膘?
A: 請檢查Package.swift
是否有在platform
內(nèi)指定版本衅斩,如有請升級 swift-tools-version
定義行。
Q: 我的資源文件在添加 process
以后仍然有警告
A: 請使用文件夾名字或指定每一個文件的名字援雇,通配符并不能很好的工作矛渴。
Q: 我在定義 Package.swift
的時候沒有找到你說的這個幾個字段
A: 請升級第一行的 swift-tools-version
。
Q: 聯(lián)網(wǎng)拉取 Swift Package
無法完成
A: 請考慮清除~/Library/Caches/org.swift.swiftpm/
惫搏,并換個好一些的網(wǎng)絡(luò)。如果依然失敗請刪除 Package.resolved
文件