Swift 从入门到放弃-基础学习

介绍

最近打算重新学习一下 Swift ,因为之前一直使用 objc 的思维模式写 Swift ,另外想进一步了解 Swift 语言的设计实现,应该能丰富我的认知。 特意找了个旧设备装上 macOS 10.14.6 系统,并安装上 Xcode 11 ,新版本语法特性太多,暂时先不考虑,只从早期 ABI 稳定又稍微升级过的 5.1 版本开始。

bash 复制代码
swift -version
# Apple Swift version 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15)
# Target: x86_64-apple-darwin18.7.0

Swift 编译流程

开始之前,先了解下 Swift 语言的编译流程

图中命令其实是独立的,这样画只是说明它经历过哪些环节,先是从 Swift 代码开始,编译器会对代码进行分词,解析,生成抽象语法树,这期间会进行语义分析,譬如类型检查之类的操作。 接着就是把生成的 ast 转成 sil,既然有 sil,那自然就是优化 sil,完了就生成 LLVMIR 码,然后通过 LLVM 把得到的 IR 码转成机器代码(期间会进行编译,链接库等操作,然后才生成对应平台的二进制文件)。

Swift 基本命令

swiftc 这个是编译器的命令,可以通过 swiftc --help 来查看有哪些参数。

bash 复制代码
OVERVIEW: Swift compiler

USAGE: swiftc

MODES:
  -dump-ast              Parse and type-check input file(s) and dump AST(s)
  -dump-parse            Parse input file(s) and dump AST(s)
  -dump-scope-maps <expanded-or-list-of-line:column>
                         Parse and type-check input file(s) and dump the scope map(s)
  -dump-type-info        Output YAML dump of fixed-size types from all imported modules
  -dump-type-refinement-contexts
                         Type-check input file(s) and dump type refinement contexts(s)
  -emit-assembly         Emit assembly file(s) (-S)
  -emit-bc               Emit LLVM BC file(s)
  -emit-executable       Emit a linked executable
  -emit-imported-modules Emit a list of the imported modules
  -emit-ir               Emit LLVM IR file(s)
  -emit-library          Emit a linked library
  -emit-object           Emit object file(s) (-c)
  -emit-sibgen           Emit serialized AST + raw SIL file(s)
  -emit-sib              Emit serialized AST + canonical SIL file(s)
  -emit-silgen           Emit raw SIL file(s)
  -emit-sil              Emit canonical SIL file(s)
  -index-file            Produce index data for a source file
  -parse                 Parse input file(s)
  -print-ast             Parse and type-check input file(s) and pretty print AST(s)
  -resolve-imports       Parse and resolve imports in input file(s)
  -typecheck             Parse and type-check input file(s)

OPTIONS:
  -api-diff-data-dir <path>
                          Load platform and version specific API migration data files from <path>. Ignored if -api-diff-data-file is specified.
  -api-diff-data-file <path>
                          API migration data is from <path>
  -application-extension  Restrict code to those available for App Extensions
  -assert-config <value>  Specify the assert_configuration replacement. Possible values are Debug, Release, Unchecked, DisableReplacement.
  -continue-building-after-errors
                          Continue building, even after errors are encountered
  -debug-info-format=<value>
                          Specify the debug info format type to either 'dwarf' or 'codeview'
  -debug-info-store-invocation
                          Emit the compiler invocation in the debug info.
  -debug-prefix-map <value>
                          Remap source paths in debug info
  -disable-autolinking-runtime-compatibility-dynamic-replacements
                          Do not use autolinking for the dynamic replacement runtime compatibility library
  -disable-autolinking-runtime-compatibility
                          Do not use autolinking for runtime compatibility libraries
  -disable-migrator-fixits
                          Disable the Migrator phase which automatically applies fix-its
  -driver-time-compilation
                          Prints the total time it took to execute all compilation tasks
  -dump-migration-states-dir <path>
                          Dump the input text, output text, and states for migration to <path>
  -dump-usr               Dump USR for each declaration reference
  -D <value>              Marks a conditional compilation flag as true
  -embed-bitcode-marker   Embed placeholder LLVM IR data as a marker
  -embed-bitcode          Embed LLVM IR bitcode as data
  -emit-dependencies      Emit basic Make-compatible dependencies files
  -emit-loaded-module-trace-path <path>
                          Emit the loaded module trace JSON to <path>
  -emit-loaded-module-trace
                          Emit a JSON file containing information about what modules were loaded
  -emit-module-interface-path <path>
                          Output module interface file to <path>
  -emit-module-interface  Output module interface file
  -emit-module-path <path>
                          Emit an importable module to <path>
  -emit-module            Emit an importable module
  -emit-objc-header-path <path>
                          Emit an Objective-C header file to <path>
  -emit-objc-header       Emit an Objective-C header file
  -emit-tbd-path <path>   Emit the TBD file to <path>
  -emit-tbd               Emit a TBD file
  -enable-library-evolution
                          Build the module to allow binary-compatible library evolution
  -enforce-exclusivity=<enforcement>
                          Enforce law of exclusivity
  -fixit-all              Apply all fixits from diagnostics without any filtering
  -framework <value>      Specifies a framework which should be linked against
  -Fsystem <value>        Add directory to system framework search path
  -F <value>              Add directory to framework search path
  -gdwarf-types           Emit full DWARF type info.
  -gline-tables-only      Emit minimal debug info for backtraces only
  -gnone                  Don't emit debug info
  -g                      Emit debug info. This is the preferred setting for debugging with LLDB.
  -help                   Display available options
  -import-underlying-module
                          Implicitly imports the Objective-C half of a module
  -index-file-path <path> Produce index data for file <path>
  -index-ignore-system-modules
                          Avoid indexing system modules
  -index-store-path <path>
                          Store indexing data to <path>
  -I <value>              Add directory to the import search path
  -j <n>                  Number of commands to execute in parallel
  -L <value>              Add directory to library link search path
  -l<value>               Specifies a library which should be linked against
  -migrate-keep-objc-visibility
                          When migrating, add '@objc' to declarations that would've been implicitly visible in Swift 3
  -migrator-update-sdk    Does nothing. Temporary compatibility flag for Xcode.
  -migrator-update-swift  Does nothing. Temporary compatibility flag for Xcode.
  -module-cache-path <value>
                          Specifies the Clang module cache path
  -module-link-name <value>
                          Library to link against when using this module
  -module-name <value>    Name of the module to build
  -nostdimport            Don't search the standard library import path for modules
  -num-threads <n>        Enable multi-threading and specify number of threads
  -Onone                  Compile without any optimization
  -Osize                  Compile with optimizations and target small code size
  -Ounchecked             Compile with optimizations and remove runtime safety checks
  -output-file-map <path> A file which specifies the location of outputs
  -O                      Compile with optimizations
  -o <file>               Write output to <file>
  -parse-as-library       Parse the input file(s) as libraries, not scripts
  -parse-sil              Parse the input file as SIL code, not Swift source
  -parseable-output       Emit textual output in a parseable format
  -profile-coverage-mapping
                          Generate coverage data for use with profiled execution counts
  -profile-generate       Generate instrumented code to collect execution counts
  -profile-use=<profdata> Supply a profdata file to enable profile-guided optimization
  -remove-runtime-asserts Remove runtime safety checks.
  -require-explicit-availability-target <target>
                          Suggest fix-its adding @available(<target>, *) to public declarations without availability
  -require-explicit-availability
                          Require explicit availability on public declarations
  -Rpass-missed=<value>   Report missed transformations by optimization passes whose name matches the given POSIX regular expression
  -Rpass=<value>          Report performed transformations by optimization passes whose name matches the given POSIX regular expression
  -runtime-compatibility-version <value>
                          Link compatibility library for Swift runtime version, or 'none'
  -sanitize-coverage=<type>
                          Specify the type of coverage instrumentation for Sanitizers and additional options separated by commas
  -sanitize=<check>       Turn on runtime checks for erroneous behavior.
  -save-optimization-record-path <value>
                          Specify the file name of any generated YAML optimization record
  -save-optimization-record
                          Generate a YAML optimization record file
  -save-temps             Save intermediate compilation results
  -sdk <sdk>              Compile against <sdk>
  -serialize-diagnostics  Serialize diagnostics in a binary format
  -static-executable      Statically link the executable
  -static-stdlib          Statically link the Swift standard library
  -static                 Make this module statically linkable and make the output of -emit-library a static library.
  -suppress-warnings      Suppress all warnings
  -swift-version <vers>   Interpret input according to a specific Swift language version number
  -target-cpu <value>     Generate code for a particular CPU variant
  -target-variant <value> Generate code that may run on a particular variant of the  deployment target
  -target <value>         Generate code for the given target
  -tools-directory <directory>
                          Look for external executables (ld, clang, binutils) in <directory>
  -track-system-dependencies
                          Track system dependencies while emitting Make-style dependencies
  -use-ld=<value>         Specifies the linker to be used
  -verify-debug-info      Verify the binary representation of debug output.
  -version                Print version information and exit
  -vfsoverlay <value>     Add directory to VFS overlay file
  -v                      Show commands to run and use verbose output
  -warn-implicit-overrides
                          Warn about implicit overrides of protocol members
  -warn-swift3-objc-inference-complete
                          Warn about deprecated @objc inference in Swift 3 for every declaration that will no longer be inferred as @objc in Swift 4
  -warn-swift3-objc-inference-minimal
                          Warn about deprecated @objc inference in Swift 3 based on direct uses of the Objective-C entrypoint
  -warnings-as-errors     Treat warnings as errors
  -whole-module-optimization
                          Optimize input files together instead of individually
  -working-directory <path>
                          Resolve file paths relative to the specified directory
  -Xcc <arg>              Pass <arg> to the C/C++/Objective-C compiler
  -Xlinker <value>        Specifies an option which should be passed to the linker

先写一段简单的 Swift 代码

swift 复制代码
import Foundation

print("hello, world")

然后用 -dump-ast 导出它的语法树,应该就能看到打印出来的抽象语法树。

bash 复制代码
swiftc -dump-ast main.swift

再来看看 Swift 中间代码长啥样,使用 -emit-sil 参数。

bash 复制代码
swiftc -emit-sil main.swift

至于其他命令,也是通过类似方式执行就能得到对应的结果。

基础语法

Constants 及 Variables

Swiftlet 表示常量, var 表示变量,代码长下面这样。我看官方文档说 let 表示常量,我个人认为 let 表示的是一个变量不可变,当然还是以官方为准比较好。

下面第一行代码,如果 let a 但是并不指定类型也不赋值,就会报错

swift 复制代码
let a: Int
a = 11
let b = 12
var c = 11
c = 14

编译器会提示这样的错误

swift 复制代码
let a // Type annotation missing in pattern

标识符

标识符也就是 identifier,譬如下面的 atest 就是标识符

swift 复制代码
let a = 10

func test() {}

Swift 语言的标识符不能使用数字、空白字符(譬如 tab、换行符)、箭头之类的特殊字符,不能用数字开头的这很好理解,因为编译器会很难区分当前读取的一连串字符到底是数字(譬如语言中的整型,浮点型)还是单纯的标识符。不过像下面这种代码是支持的

swift 复制代码
func 🐂🍺() -> Int {
    return 666
}
print(🐂🍺())

值类型

Integers

Swift 的整型是 Int 表示,也有对应精度的 Int8Int16Int32Int64UInt8UInt16UInt32UInt64Int 对应 32 位平台上就会自动使用 Int32,对应 64 位平台上就会使用 Int64,观感上类似 objc 的 NSInteger,如果要写一些早期 8bit 的游戏平台仿真器(譬如 NES)就会用到 UInt8 这个类型,通常在 iOS 开发中直接用 Int 就好。Swift 也可以使用类似下面这样的数值表示

swift 复制代码
let a = 16 // 十进制的 16
let b = 0b1_0000 // 二进制的 16
let c = 0o20 // 八进制的 16
let d = 0x10 // 十六进制的 16

Floating-Point Numbers

Swift 的浮点型也是可以套用其他语言的认知,分 FloatDouble 两种,另外浮点型支持科学计数法

swift 复制代码
let a = 12.0
let b = 1.2e1 // 科学计数法
a == b // true
let c = 0x06p1 // 6 * (2 ^ 1)
a == c // true
let d = 0x06p-1 // 6 * (2 ^ -1)
d == 3.0 // true
let e = 0x1.1p0 // (1 + 1.0 / 16.0) * (2 ^ 0)

Numeric Type Conversion

Swift 的数字(譬如 Int16Int8IntDouble)之间做运算,需要转成相同的类型。其实这些在其他语言看来是基本类型的东西(譬如 Int)在 Swift 看来是结构体,它们的 + 号也是 Int/Double 结构体上定义的静态方法,然后这个静态方法限定了左右两边相加的类型,所以如果要把一个整型跟一个浮点型相加,那就把其中一个数的值给另一个数相同类型的构造函数构造出实例,于是下面就变成了 Double(a) + b

swift 复制代码
let a = 3
let b = 0.14
// let result = a + b // Binary operator '+' cannot be applied to operands of type 'Int' and 'Double'
let result = Double(a) + b // 3.14

如果不指定类型,直接把两个 literal(字面量)相加,就不需要写构造

swift 复制代码
let result = 3 + 0.14 // 3.14

Tuples

苹果官方文档上有下面一段代码,因为我学过 Rust ,一看这个东西,就感觉它跟 Rust 用法应该差不多。

swift 复制代码
let http404Error = (404, "Not Found")

应该是通过 http404Error.0http404Error.1 这种方式访问数据,然后之前用过一段时间 Swift,知道它也有模式匹配,所以肯定也能这样取数据

swift 复制代码
let (code, message) = http404Error

因为很多 OCaml 风格的语言(譬如 Rust , ReScript )都用 _ 的符号表示不使用某个值,所以下面这段代码理所当然地从脑海中冒出来。

swift 复制代码
let (code, _) = http404Error

当然官方文档还给出了另一种构造跟访问元组的方式

swift 复制代码
let http200Status = (statusCode: 200, description: "OK")
print(http200Status.statusCode)
print(http200Status.1)

String

Swift 的字符串字面量直接用双引号包裹就行,这玩意还支持 + 之类的运算符,还有类似模板字符串的东西,多行字符串也是可以的。Swift 字符串操作太多了,后面再细看一下。

swift 复制代码
var str = "some str"
str += " end"
str = "\(str)."  // "some str end."
str = """
line break
"""

Character

如果给一个字符串指定 Character 类型,就得到一个字符了,当然当前字符串超出一个字符,就会收到编译器错误

swift 复制代码
let char: Character = "🍺"
print(char) // 🍺
let c: Character = "123" // Cannot convert value of type 'String' to specified type 'Character'

未完待续

相关推荐
报错小能手2 天前
ios开发方向——swift并发进阶核心 Task、Actor、await 详解
开发语言·学习·ios·swift
用户79457223954133 天前
【AFNetworking】OC 时代网络请求事实标准,Alamofire 的前身
objective-c·swift
报错小能手3 天前
SwiftUI 框架 认识 SwiftUI 视图结构 + 布局
ui·ios·swift
东坡肘子3 天前
被 Vibe 摧毁的版权壁垒,与开发者的新护城河 -- 肘子的 Swift 周报 #131
人工智能·swiftui·swift
报错小能手3 天前
ios开发方向——swift错误处理:do/try/catch、Result、throws
开发语言·学习·ios·swift
小夏子_riotous3 天前
openstack的使用——5. Swift服务的基本使用
linux·运维·开发语言·分布式·云计算·openstack·swift
mCell4 天前
MacOS 下实现 AI 操控电脑(Computer Use)的思考
macos·agent·swift
用户79457223954134 天前
【DGCharts】iOS 图表渲染事实标准——8 种图表类型、高度可定制,3 行代码画出一条折线
swiftui·swift
chaoguo12344 天前
Any metadata 的内存布局
swift·metadata·value witness table
tangweiguo030519875 天前
SwiftUI布局完全指南:从入门到精通
ios·swift