Swift知识一览(一)
一、概览
Swift是苹果主推的Apple系列产品的开发编程语言,相较于早期的Objective-C,Swift是一门类型安全的且具有可扩展性的编程语言。
Swift是开源的,apple在github上的开源Swift地址:https://github.com/apple/swift。
Swift主要是使用C++编写。
swift的官网:https://www.swift.org/
如官网所说,Swift是一个对入门者友好,也对资深专家强有力支持的具有多用途的编程语言。它快捷、现代、安全且便于编写。
使用领域:
- Apple系列平台:比如iOS、macOS、ipadOS等其他平台;
- 跨平台命令行:因其语法简洁、安全且轻量;
- 服务端和网络:因其内存占用小,启动快、高性能。
目前由于Swift的ABI(Application Binary Interface,应用程序二进制接口)稳定,所以近期并没有新版本频繁更新。
目前最新的稳定版本是5.10。
解释:
ABI是应用程序与操作系统之间的底层接口,相关内容是目标文件格式、数据类型的大小/布局/对齐、函数调用约定等。
API是应用程序编程接口。
二、安装
2.1 MacOS系统
直接安装新版的Xcode,Xcode内置了新版的Swift。
2.2 Linux系统
使用Docker容器就可以获取到官方的Swift镜像。
Docker容器的基本知识可以在官网学习,也可以看我的博客:https://www.xinwei.ltd/article/83 查看基本命令。
2.3 Windows系统
使用包管理器,比如WinGet。
当在电脑中安装了Swift环境后,就可以在终端通过以下类似指令执行swift的操作。

三、如何针对不同领域方向学习
3.1 命令行
可以参考官方教程:https://www.swift.org/getting-started/cli-swiftpm/
3.2 跨平台库
可以参考官方教程:https://www.swift.org/getting-started/library-swiftpm/
3.3 Web服务
可以参考官方教程:https://www.swift.org/getting-started/vapor-web-server/
3.4 iOS和macOS
可以参考官方教程:https://www.swift.org/getting-started/swiftui/
四、Swift的编译过程
可以通过在Xcode的Swift代码断点,查看汇编代码,Swift的编译前端是swiftc。以下图片可以揭示编译流程和编译前后端的概况。(以下图片来自于网络)


从图中可以看出,代码编译的过程主要分为以下几步:
1.生成语法树:swiftc -dump-ast main.swift
2.生成简洁的SIL代码:swiftc -emit-sil main.swift
3.生成LLVM IR代码:swiftc -emit-ir main.swift -o main.ll
4.生成汇编代码:swiftc -emit-assembly main.swift -9 main.s
五、基本语法和约定
5.1 类型声明
- let 声明不可变变量
- var 声明可变变量
5.1.1 常量
只能赋值一次,不要求在编译时期确定,但是使用之前必须赋值一次。
swift
let num = 1
let num1: Int
num1 = 20
// 以下编译会报错
let a: Int
print(a)
5.1.2 标识符
可以使用任意字符作为常量名、变量名、函数名,但是不能以数字开头,也不能包含空白符、制表符、箭头等特殊字符。
swift
let 🤭 = "emoj1"
5.2 数据类型
在Swift中有2种常见的数据类型
- 值类型
- 引用类型
5.2.1 值类型
包括以下:
- 枚举enum、可选类型(本身也是枚举)
- 结构体struct,包括常见的String、Array、Dictionary、Int、Float、Double、Bool、Character、Set
整型说明:
Int在32位平台上等价于Int32,在64位平台上等价于Int64。
浮点数说明:
Float有32位,精度只有6位
Double有64位,精度至少有16位
5.2.2 引用类型
class类型
5.2.3 字面量
其实就是直接确定的值
比如:
swift
let num = 1
let str = "hello world"
let decimal = 0x01 // 16进制
let hex = 0o02 // 8进制
let binary = 0b03 // 2进制
let doubleHex = 0xFp2 // 16进制,表示15 * 2(^2),十进制的60
let doubleHex = 0xFp-2 // 16进制,表示15 * 2(^-2),十进制的3.75
// 数组
let arr = [1, 2, 3]
// 字典
let dic = ["name" : "小明", "age" : 10]
5.2.4 类型转换
swift
let num: Int = 1
let num1: UInt8 = 2
let sum = num + Int(num1)
// 下面也是可以的,数字字面量本身没有明确类型
let sum = 1 + 3.14
5.2.5 元组(Tuple)
swift
let response = (200, "success")
print("code is \(response.0),message is \(response.1))")
// 类似于前端的析构
let (code, msg) = response
let response1 = (code: 200, msg: "success")
print("code is \(response1.code),message is \(response1.msg))")
5.3 流程控制相关
5.3.1 if-else语句
if中的语句,只能是bool类型,而不能是像OC中1之类的数字
swift
let num = 10
if num > 10 {
// ...
} else if num < 10 {
// ...
} else {
}
5.3.2 while语句
swift
var num = 1
white num < 5 {
num += 1
}
var age = 10
repeat { // 相当于do-while
age += 1
} while age < 100
5.3.3 for循环
swift中的for循环,可以有for-in的写法,并且配合区间运算符,轻巧简洁
swift
let nums = [1, 2, 3, 4, 5]
for num in 0...3 {
print("num is \(nums[num])")
}
// 需要注意以下的循环
for var i in 0...3 {
i += 1
print(i)
} // 打印的是:1,2,3,4
// 可以省略迭代变量
for _ in 0...3 {
// do something 4 times
print("*")
}
5.3.4 区间运算符
- 闭区间:
0...3:相当于 >= 0,并且 <= 3之间的区间
区间的类型是:ClosedRange<T> - 半开区间:
0..<3:相当于 >= 0,并且 < 3之间的区间
区间的类型是:Range<T> - 单侧区间
swift
let minRange = ...10 // <=10以下的区间范围
let maxRange = 10... // >=10以上的区间范围
print(minRange.contains(5)) // true
print(minRange.contains(11)) // false
print(maxRange.contains(-1)) // false
区间的类型是:PartialRangeThrough<T>
这里解释以下类型中的T,后面会解释Swift中的这种泛型。
区间运算符也可以使用字符串,通过ASCII来判断值
swift
let range = "a"..."f" // ClosedRange<String>
range.contains("c") // true
range.contains("z") // false
这里扩充一下,有一个函数,表示有间隔的区间(类似于步进器):
swift
let start = 0
let interval = 2
let max = 100
for num in stride(from: start, through: max, by: interval) {
print("100以内的正偶数:\(num)")
}// 0, 2, 4, 6, ..., 100
5.3.5 switch语句 & where
swift
let num = 5
switch num {
case 1: // 不允许写{}
print("是1")
break // 可以不写,也不会贯穿case
case 2:
print("是2")
fallthrough // fallthrough可以实现条件贯穿
default: // 不允许写{}
print("非1非2")
}
- 针对switch中的表达式,需要case枚举尽,或者使用default。
default在case穷举完之后,可以不使用default。 - case和default后面至少需要一条语句,或者break一下。
- case支持复合条件,或者case叠加
swift
let char = "a"
switch char {
case "a", "A":
print("都是a")
default:
print("非a")
}
switch char {
case "a":
case "A":
print("都是a")
default:
print("非a")
}
- 可以使用区间或者元组
swift
let num = 10
switch num {
case 1...5:
print("1<=x<=5以内")
case 5<..10:
print("5<x<=10以内")
default:
print("unknown")
}
let pt = (0, 10)
switch pt {
case (0, 0):
print("1")
case (_, 10): // 下划线代表忽略对应位置的值
print("纵轴是10的点")
case (0, _):
print("横轴是0的点")
case (-1..<0, 10...20):
print("在阴影区")
default:
print("unknown")
}
// 值绑定
let pt2 = (0, 10)
switch pt2 {
case (let x, 0):
print("横轴坐标\(x)")
case (0, let y):
print("纵轴坐标\(y)")
case let (x, y):
print("unknown point (\(x), \(y))")
}
- 配合where使用
swift
let turple = (0, 10)
switch turple {
case (let x, 10): // 绑定到x
print("x is \(x), y is 10")
case (0, let y): // 绑定到y
print("y is \(y), x is 0")
default:
print("anything")
}
let turple1 = (0, 10)
switch turple1 {
case let (x, y) where x == y: // where条件
print("x is equal to y")
default:
print("anything")
}
let nums = [1, 2, 3, 4]
for num in nums where num > 2 { // 类似于continue
print("num is \(num)")
}
5.3.6 标签语句
swift
upper: for i in 0...3 {
for j in 0...3 {
if j > 3 {
continue upper
}
if i == 3 {
break upper
}
print("i is \(i), j is \(j)")
}
}
5.4 函数
5.4.1 定义&声明
swift
func dosomthing() {
// code
}
// 等价于
func dosomething() -> Void {
// code
}
// 等价于
func dosomething() -> () {
}
// 带参数
func dosomething(param1: Int, param2: Int) -> Int {
return param1 + param2
}
// 隐式返回
// 如果整个函数体是一个单一表达式,那么函数会隐式返回这个表达式
func dosomething(a: Int, b: Int) -> Int {
a + b
}
dosomthing(a: 1, b: 2) // 3
// 返回元组
func getValues(a: Int, b: Int) -> (sum: Int, average: Int) {
let sum = a + b
return (sum, sum >> 1)
}
let result = getValues(a: 10, b: 2)
print(result.sum) // 12
print(result.average) // 6
// 函数参数标签
func eat(thing food: String) {
print("eat \(food)")
}
eat(thing: "苹果")
// 省略参数
func eat(_ food: String) {
print("eat \(food)")
}
eat("苹果")
5.4.2 参数相关
swift
// 默认参数值
func addUser(name: String = "小明", age: Int, score: Int = 100) {
print("姓名:\(name),年纪:\(age),成绩:\(score)")
}
addUser(name: "Tom", age: 10, score: 80)
addUser(name: "Tom", age: 10)
addUser(age: 10, score: 80)
addUser(age: 10)
// 如果省略参数标签,此时应注意若,语义模糊,编译器编译时或运行时报错
func addUser(_ name: String = "小明", age: Int, _ score: Int = 100) {
}
// 可变参数,以print为例就是一个可变参数的函数
// 一个函数最多只能有1个可变参数
// 紧跟在可变参数后面的参数不可省略参数标签
func addNums(numbers: Int...) -> Int { // 此处的Int...表示一组Int参数
var sum = 0
for num in numbers {
sum += num
}
return sum
}
sum(numbers: 1, 2, 3, 4)
func sumTest(_ a: Int..., name: String, _ age: Int) {
}
// print函数的定义:
/// - Parameters:
/// - items: Zero or more items to print.
/// - separator: A string to print between each item. The default is a single space (`" "`).
/// - terminator: The string to print after all items have been printed. The
/// default is a newline (`"\n"`).
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
5.4.3 inout标记的参数地址传递
使用inout定义一个输入输出参数,可以在函数内部修改外部实参的值。
swift
func swapValue(x: inout Int, y: inout Int) {
// 第一种方式
let temp = x
x = y
y = temp
// 第二种方式,使用元组
(x, y) = (y, x)
}
var x = 10, y = 20
swapValue(&x, &y)
注意:
- 可变参数(比如:Int...)不能标记为inout
- inout参数不能有默认值
- inout参数只能传入可以被多次赋值的
- inout参数的本质是地址传递(引用传递)
5.4.4 函数重载
定义:函数名相同,但参数个数不同,或者参数类型不同,或者参数标签不同。
swift
func setPosition(x: Float, y: Float) -> (Float, Float) { }
func setPosition(x: Float, y: Float, z: Float) -> (Float, Float) { }
func setPosition(x: Float, y: Float) -> (Float, Float) { }
func setPosition(x: Int, y: Float) -> (Float, Float) { }
func setPosition(_ x: Float, _ y: Float) -> (Float, Float) { }
func setPosition(a: Int, b: Float) -> (Float, Float) { }
注意以下的代码编译器会报错(非重载,因为返回值的类型和函数重载无关):

默认参数值和函数重载一起使用产生二义性时,编译器不会报错
swift
func test(a: Int, b: Int) -> Int {
a + b
}
func test(a: Int, b: Int, c: Int = 3) {
a + b + c
}
test(a: 1, b: 2) // 调用的是第一个
注意如果可变参数、省略参数标签、函数重载一起使用产生二义性时,编译器可能会报错。
5.4.5 内联函数 inline function
开启编译器优化(Release模式下默认开启),某些函数会被编译器编译为内联函数,也就是将函数的调用体展开执行。
(注意:如果函数体比较长,或者含有递归调用,或者含有动态派发,一般不会被自动内联)
一般情况下,Swift不需要手动处理内联条件。但是Swift也提供了手动处理内联函数的语义。
swift
// 这种不会被内联,即使开启编译器优化
@inline(never) func test() {
print("test")
}
// 开启编译器优化后,代码很长的情况,也会被内联。
// 但是如果有递归调用、动态派发,还是不会被内联。
@inline(__always) func test() {
print("test")
}
5.4.6 函数类型
函数是有类型的,由形参类型、返回值类型组成
swift
func dosomething() {} // 函数类型是:() -> Void 或者 () -> ()
// 另外提一句,Void是元组()的类型别名,点击Void进去或者看Swift源码可证
func add(a: Int, b: Int) -> Int {} //函数类型是:(Int, Int) -> Int
// 声明
var fn: (Int, Int) -> Int = add
fn(1, 2) // 3 这个时候不需要a或者b形参,因为函数类型不包括形参
5.4.7 函数作参数,或者作返回值
swift
func add(a: Int, b: Int) -> Int {
a + b
}
func minus(a: Int, b: Int) -> Int {
a - b
}
func decideToHandle(_ fn: (Int, Int) -> Int, a: Int, b: Int) {
let result = fn(a, b)
print("result is \(result)")
}
// 作加法
decideHandle(add, a: 1, b: 2)
// 作减法
decideHandle(minus, a: 1, b: 2)
返回值如果是函数类型,这种函数叫作高阶函数(higher-order function)
swift
func add(a: Int, b: Int) -> Int {
a + b
}
func minus(a: Int, b: Int) -> Int {
a - b
}
func handle(_ useAdd: Bool) -> (Int, Int) -> Int {
useAdd ? add : minus
}
// 作加法
handle(true)(1, 2) // 3
// 作减法
handle(false)(1, 2) // -1
5.5 类型别名typealias
作用是用来给类型取别名的。
比如:
swift
typealias MyInt = Int8
let num: MyInt = 4
typealias CustomFn = (Int, Int) -> Int
func add(a: Int, b: Int) -> Int {
a + b
}
CustomFn = add
CustomFn(1, 2) // 3
5.6 嵌套函数
指的是在函数内部再定义函数。
如:
swift
func handle(_ useAdd: Bool) -> (Int, Int) -> Int {
func add(a: Int, b: Int) -> Int {
a + b
}
func minus(a: Int, b: Int) -> Int {
a - b
}
return useAdd ? add : minus
}
handle(true)(1, 2) // 3
handle(false)(1, 2) // -1
这是《Swift知识一览》系列的第一篇文章,若感兴趣,请关注后续发表的文章。
暂无评论,快来发表第一条评论吧