本工具使用的开源库

本工具代码中捆绑了 1 个开源库。

Swift 速查 — 简明参考

Swift 5.9 语法、值类型、协议与最常用标准库速查手册,覆盖约 80% 日常场景。

Sw

Swift Swift 5.9

LLVM (swiftc / Swift toolchain) · 多范式(协议导向 · OO · 函数式) · 静态 · 强类型

学习路径

先学会 print 与变量绑定 → 掌握值类型(struct/enum)与可选值 Optional → 深入函数、闭包与集合 → 理解协议导向设计与扩展 → 用 do-catch 与 throws 处理错误 → 用 async/await 与 DispatchQueue 写并发 → 再按需学 URLSession、日期与构建测试。FAQ 节适合回头避坑。

1.Hello World 与构建环境

运行 Swift 程序、SwiftPM 项目与工具链。

最小程序

顶层代码即程序入口。print 输出。import 导入模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 新建文件 main.swift:
import Foundation
print("Hello, world!")
// 运行:
// swift main.swift
// 编译运行:
// swiftc main.swift -o app
// ./app
// 要点:
// 顶层语句直接执行
// 无需 main 函数(脚本模式)
// print 自动换行
// import Foundation 常用

运行与构建

swift 直接运行脚本。swiftc 编译。swift run 运行包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 直接运行脚本:
// swift script.swift
// 编译为可执行:
// swiftc main.swift -o app
// ./app
// 运行 SwiftPM 包:
// swift run
// 指定可执行目标:
// swift run MyTool
// 编译优化:
// swiftc -O main.swift
// 编译所有源文件:
// swiftc *.swift
// 调试信息:
// swiftc -g main.swift
// 检查版本:
// swift --version

SwiftPM 项目

swift package init 初始化。Package.swift 清单。Sources 源码目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建可执行包:
// swift package init --type executable
// 创建库包:
// swift package init --type library
// 结构:
// Package.swift 清单
// Sources/ 源码
// MyTool/ 目标目录
// Tests/ 测试
// Package.swift:
// swift-tools-version:5.9
// import PackageDescription
// let package = Package(
// name: "MyTool",
// targets: [.executableTarget(
// name: "MyTool")]
// )
// 构建:swift build
// 运行:swift run
// 测试:swift test

import 导入

import 导入模块。Foundation 常用。按需导入子模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 导入标准模块:
import Foundation
import UIKit // iOS 界面
import SwiftUI // SwiftUI 界面
// Foundation 提供:
// 字符串、日期、文件、JSON
// 数值、集合的增强
// 子模块:
import Foundation.NSURL
// 多平台条件导入:
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif
// 导入自己模块的库:
// import MyLibrary
// import 语句在文件顶部

输出与插值

print/print(items:)。字符串插值 \(值)。分隔符与终止符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let name = "Nick"
let age = 30
// 字符串插值:
print("\(name) 今年 \(age) 岁")
// 多值输出:
print(1, 2, 3) // 空格分隔
print("a", "b", separator: "-") // a-b
print("x", terminator: "") // 不换行
// 调试输出:
debugPrint(name)
// 格式控制:
let pi = 3.14159
print(String(format: "%.2f", pi)) // 3.14
// 数组/字典直接打印:
print([1, 2, 3])
print(["a": 1])
// 自定义类型实现 CustomStringConvertible

命令行参数

CommandLine.arguments 获取参数。第一个是程序路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Foundation
// 全部参数:
let args = CommandLine.arguments
print("共 \(args.count) 个参数")
for (i, a) in args.enumerated() {
print("\(i): \(a)")
}
// 业务参数:
// args[0] 是程序路径
// 从 args[1] 开始是参数
if args.count > 1 {
let name = args[1]
print("你好 \(name)")
}
// 简单解析:
// 复杂参数用 ArgumentParser
import ArgumentParser // 需依赖
// 退出:
// exit(0) / exit(1)

多文件

同一包多个源文件共享类型。internal 访问级别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Sources/App/main.swift
let greeter = Greeter(name: "Swift")
greeter.greet()
// Sources/App/Greeter.swift
// 同包内类型自动可见:
struct Greeter {
let name: String
func greet() {
print("你好,\(name)!")
}
}
// 访问级别:
// private 文件内
// internal 模块内(默认)
// public 模块外
// 编译:
// swiftc main.swift Greeter.swift
// SwiftPM 自动包含 Sources/
// 文件名无关,类型名唯一即可

Xcode 集成

Xcode 项目组织。target、scheme。iOS 开发工作流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 项目结构:
// MyApp.xcodeproj
// Sources/
// Resources/
// Tests/
// target:构建单元
// iOS App / watchOS App
// Framework / Unit Test
// scheme:构建+运行组合
// 命令行构建:
// xcodebuild -scheme MyApp build
// 运行测试:
// xcodebuild test -scheme MyApp
// 模拟器:
// xcrun simctl list devices
// 导出 IPA:
// xcodebuild archive
// SwiftPM 包可直接拖入
// 与 SwiftPM 区别:
// Xcode 处理签名与设备

2.变量与常量

var/let 绑定、类型推断、作用域与命名。

var 与 let

let 常量不可变。var 变量可变。优先使用 let。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 常量:
let maxCount = 100
// maxCount = 200 // 错误:不可变
// 变量:
var score = 0
score = 10 // 允许修改
// 类型推断:
let name = "Swift" // String
let number = 42 // Int
// 显式类型:
let count: Int = 5
var price: Double = 3.5
// 命名原则:
// 能 let 就 let
// 需要修改才 var
// 并发环境不可变性更安全
// 集合用 var 可变、let 只读

类型推断

编译器根据字面量与上下文推断类型。复杂场景显式标注。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 字面量推断:
let a = 42 // Int
let b = 3.14 // Double
let c = "hi" // String
let d = true // Bool
// 上下文推断:
var array: [Int] = []
array.append(1) // 元素推断 Int
// 字面量可多类型:
let i: Int = 42
let f: Double = 42 // 允许
let u: UInt8 = 42
// 显式标注场景:
// 泛型、可选值、复杂表达式
// 编译器报错时补充标注
// 类型别名:
typealias Age = Int
let age: Age = 30
// 快捷键 Option+Click 查看类型

显式类型

冒号后标注类型。提高可读性。转换与字面量兼容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 基础标注:
let name: String = "Swift"
let count: Int = 10
let ratio: Double = 0.5
// 可选值标注:
var optional: String? = nil
// 集合标注:
var list: [Int] = [1, 2]
var dict: [String: Int] = [:]
// 元组标注:
var pair: (Int, String) = (1, "a")
// 函数类型标注:
var handler: (Int) -> Void
// 何时需要:
// 空集合无法推断
// 协议类型、泛型
// 公共 API 必须标注
// 有助于文档与编译期检查

常量

let 声明常量。引用类型常量不可变引用。编译期常量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 基本常量:
let days = 7
// 常量数组/字典:
let fixed = [1, 2, 3]
// fixed.append(4) // 错误:不可变
// 常量引用类型:
class Counter { var n = 0 }
let c = Counter()
// c 不可变,但对象属性可变:
c.n += 1 // 允许
// 全局常量:
let appName = "GuruToolkit"
// 编译期常量:
// static let / enum 命名空间
enum Config {
static let maxRetry = 3
}
print(Config.maxRetry)
// 常量可计算:
let total = days * 24

遮蔽

内层作用域同名变量遮蔽外层。if/switch 局部常量常见。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let x = 10
// 内层遮蔽:
if true {
let x = 20 // 遮蔽外层
print(x) // 20
}
print(x) // 10
// 常见模式:
// 可选绑定同名:
var value: Int? = 5
if let value = value {
print(value) // 新常量 5
}
// 循环变量遮蔽:
for x in 1...3 {
print(x) // 1 2 3
}
// 注意:
// 遮蔽降低可读性
// 复杂函数避免多层遮蔽
// 可选绑定利用遮蔽安全解包

作用域

{} 定义作用域。内层可访问外层。外层不可访问内层。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let outer = 1
// 块作用域:
{
let inner = 2
print(outer) // 内层可访问外层
print(inner)
}
// print(inner) // 错误:已离开作用域
// 函数作用域:
func demo() {
let local = "函数内"
print(local)
}
// 循环作用域:
for i in 1...3 {
let sq = i * i
print(sq)
}
// 同名规则:
// 同一作用域不能重复声明
// 嵌套作用域可遮蔽
// 生命周期:
// 作用域结束变量销毁
// 全局变量程序生命周期内存在

命名规范

Swift 命名约定。camelCase、清晰动词、避免缩写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 变量/常量:camelCase
let userName = "nick"
var totalCount = 0
// 类型:CamelCase
struct UserAccount {}
class NetworkManager {}
enum ColorChoice {}
// 协议:动词/形容词命名
protocol CanGreet {}
protocol Named {}
// 布尔属性:is/has 前缀
var isEnabled = true
var hasPermission = false
// 方法:动词开头
func saveData() {}
func loadConfig() {}
// 官方 API 设计规范:
// 名称读作句子
// 避免缩写与模棱两可
// 文档注释 /// 说明用途

类型转换

数值类型显式转换。init() 构造器。字符串与数值互转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 数值转换(显式):
let i = 42
let d = Double(i) // 42.0
let u = UInt8(i) // 42
// 不自动隐式转换:
// let x = 1 + 2.0 // 错误
let x = Double(1) + 2.0 // 正确
// 字符串转数值:
let s = "42"
let n = Int(s) // Int? 可选
let f = Double("3.14") // Double?
// 数值转字符串:
let str = String(42)
let str2 = "\(3.14)"
// 可选解包:
if let n = Int(s) {
print(n)
}
// 截断:Int(3.9) // 3

3.类型系统

基础类型、元组、结构体、枚举与可选值。

基础类型

Int、Double、Bool、String、Character。值类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 整数:
let i: Int = -10
let u: UInt = 10
// 浮点:
let f: Float = 1.5 // 32 位
let d: Double = 3.14159 // 64 位
// 布尔:
let t: Bool = true
let f2: Bool = false
// 字符串与字符:
let s: String = "hello"
let c: Character = "A"
// 类型别名:
let whole: Int = 42
// 整数范围:
print(Int.min, Int.max)
// 标准库类型:
// Int/Double 是值类型
// 赋值拷贝而非引用
// 方法调用:i.description

整数类型

Int8-Int64、UInt 系列。平台 Int。溢出处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 有符号:Int8 Int16 Int32 Int64
// 无符号:UInt8 UInt16 UInt32 UInt64
// 平台默认:Int(64 位)
let a: Int8 = -128
let b: UInt8 = 255
let c: Int64 = 9_223_372_036_854_775_807
// 下划线分隔:
let big = 1_000_000
// 溢出:
// 默认溢出报错(编译期字面量)
// 运算溢出用溢出运算符:
var x: UInt8 = 250
x = x &+ 10 // 回绕 4
// &- &* &/ 溢出运算
// 检查溢出:
// x.addingReportingOverflow(10)
// 优先级明确时类型标注

浮点数

Float/Double 精度。CGFloat 界面坐标。运算与特殊值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Double 64 位 / Float 32 位
let d = 3.141592653589793
let f: Float = 3.14
// 科学计数:
let e = 1.5e3 // 1500.0
// 字面量推断为 Double
// 运算:
let sum = d + 1.0
let sqrt = d.squareRoot()
let pow = pow(2.0, 10.0)
// 特殊值:
let nan = Double.nan
let inf = Double.infinity
nan.isNaN // true
inf.isInfinite // true
// 比较:
// 浮点相等比较需容忍误差
let a = 0.1 + 0.2 // 0.30000000000000004
// 格式化:
String(format: "%.2f", a)
// 取整:
Int(3.7) // 3
3.7.rounded() // 4.0

元组

多元复合值。命名元素。解构。轻量数据分组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 基础元组:
let pair = (1, "one")
print(pair.0) // 1
print(pair.1) // "one"
// 命名元素:
let user = (name: "Nick", age: 30)
print(user.name)
print(user.age)
// 解构:
let (code, message) = (200, "OK")
print(code)
// 忽略部分:
let (x, _) = (1, 2)
// 类型标注:
let point: (x: Double, y: Double) = (0, 0)
// 函数返回多值:
func div(a: Int, b: Int) -> (q: Int, r: Int) {
(a / b, a % b)
}
let result = div(a: 10, b: 3)
print(result.q, result.r)
// 字典遍历解构元组

结构体

struct 值类型。自动成员构造器。属性与方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 定义结构体:
struct Point {
var x: Double
var y: Double
// 方法:
func distance() -> Double {
(x * x + y * y).squareRoot()
}
// 可变方法:
mutating func moveBy(dx: Double) {
x += dx
}
}
// 自动构造器:
let p = Point(x: 1, y: 2)
print(p.distance())
// 值类型拷贝:
var p2 = p
p2.x = 100
print(p.x) // 1,p 不受影响
// 计算属性见 OOP 节
// 首选 struct 而非 class

枚举

enum 相关值分组。关联值。原始值。穷尽匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 简单枚举:
enum Direction {
case north, south, east, west
}
// 关联值:
enum Result2 {
case success(Int)
case failure(String)
}
// 原始值:
enum Color: String {
case red = "#FF0000"
case green = "#00FF00"
}
// 使用:
let dir = Direction.north
switch dir {
case .north: print("北")
case .south: print("南")
case .east: print("东")
case .west: print("西")
}
// 关联值提取:
let r = Result2.success(200)
if case .success(let v) = r {
print(v)
}
// 原始值:
print(Color.red.rawValue)
// 方法可加在枚举上

可选值

Optional 表示可能为空。T? 语法。nil 表示无值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 声明可选值:
var name: String? = nil
var age: Int? = 30
// 赋值:
name = "Nick"
// 解包方式:
// 1. 强制解包(危险):
// print(name!)
// 2. 可选绑定:
if let n = name {
print(n)
}
// 3. 空合运算符:
let display = name ?? "未知"
// 4. 可选链:
// name?.count
// 判断为空:
if name == nil { print("无名字") }
// 实际类型:
// Optional<String> 是枚举
// 含 .some 和 .none

嵌套类型

类型内定义类型。命名空间组织。枚举携带相关类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 嵌套类型:
struct Player {
// 枚举嵌套在结构体内:
enum State {
case idle, running, dead
}
var state: State = .idle
// 类型别名嵌套:
typealias Health = Int
var health: Health = 100
}
// 使用:
let p = Player()
let s: Player.State = .running
print(s)
// 访问嵌套类型:
// Player.State
// 嵌套分类组织:
enum HTTP {
enum Status {
static let ok = 200
static let notFound = 404
}
}
print(HTTP.Status.ok)
// 好处:
// 相关类型就近定义
// 减少顶层命名冲突
// 表达“属于谁”的关系
// 深嵌套保持合理

4.值类型与引用

值语义、类引用、写时复制与内存布局。

值 vs 引用

struct 值语义拷贝。class 引用共享。语言核心区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 值类型:
struct Point { var x = 0 }
var a = Point()
a.x = 10
var b = a // 拷贝
b.x = 99
print(a.x) // 10,独立
// 引用类型:
class Box { var value = 0 }
let c = Box()
c.value = 5
let d = c // 共享引用
d.value = 100
print(c.value) // 100,同一对象
// 选择原则:
// 值类型默认优先
// 需要共享/继承用 class
// 需要比较相等语义用值类型
// struct 在 Swift 中占主导

class 语义

class 引用类型。身份唯一。可变性与线程共享。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 类定义:
class Counter {
var count = 0
}
// 引用共享:
let c1 = Counter()
let c2 = c1
c2.count += 1
print(c1.count) // 1,同一对象
// 恒等比较:
// === 比较引用相同
if c1 === c2 { print("同一实例") }
// == 比较值(需 Equatable)
// 引用类型的可变性:
// 常量引用可修改属性
// 多线程共享需注意同步
// 类支持继承(OOP 节)
// 类有 deinit 析构

写时复制

数组等值类型内部共享存储。修改时才真正拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 数组:
var a = [1, 2, 3]
var b = a // 共享内部存储
print(a) // [1, 2, 3]
// 修改时才拷贝:
b.append(4)
print(a) // [1, 2, 3] 不变
// 写时复制优化:
// 赋值几乎零成本
// 首次修改才分配拷贝
// 大数组多次赋值高效
// 自定义类型可优化:
// 用 isKnownUniquelyReferenced
// 检查引用唯一性
// 字符串/字典同样优化
// 值语义 + 高性能兼具

恒等与相等

=== 引用恒等。== 值相等(Equatable)。区别重要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 恒等 ===:
class Person { var name: String
init(name: String) { self.name = name }
}
let p1 = Person(name: "Nick")
let p2 = p1
let p3 = Person(name: "Nick")
// 恒等:
print(p1 === p2) // true 同一对象
print(p1 === p3) // false 不同对象
// 值相等(需 Equatable):
struct Point: Equatable {
var x: Int, y: Int
}
let q1 = Point(x: 1, y: 2)
let q2 = Point(x: 1, y: 2)
print(q1 == q2) // true 值相同
// 规则:
// 引用类型用 === 判断同一
// 值类型用 == 判断相等
// 自定义 Equatable 实现
// 哈希与相等一致(Hashable)

weak 与 unowned

weak 弱引用不增计数。unowned 无主引用。打破循环引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Person {
var pet: Pet?
deinit { print("Person 释放") }
}
class Pet {
// weak 不持有:
weak var owner: Person?
deinit { print("Pet 释放") }
}
var nick: Person? = Person()
var dog: Pet? = Pet()
nick?.pet = dog
dog?.owner = nick
// 循环引用已打破:
nick = nil // 释放
print("nick 已释放")
// weak 必须 var、可选
// unowned:假定始终有值
// class Restaurant {
// unowned var chef: Chef
// }
// 访问已释放 unowned 崩溃
// 优先 weak

inout 参数

inout 参数引用传入。函数内修改原值。& 调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// inout 函数:
func swapValues(_ a: inout Int, _ b: inout Int) {
let tmp = a
a = b
b = tmp
}
// 调用:
var x = 1
var y = 2
swapValues(&x, &y)
print(x, y) // 2 1
// 修改数组元素:
func increment(_ n: inout Int) {
n += 1
}
var nums = [10, 20]
increment(&nums[0])
print(nums) // [11, 20]
// 限制:
// 不能传字面量/常量
// 不能同时传同一变量两次
// 属性需 var 可变
// 与引用类型参数的区别:
// inout 是值进出的通道

指针互操作

UnsafePointer 与 C 互操作。内存管理责任。危险区域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 获取变量指针:
var number = 42
// withUnsafePointer 作用域:
withUnsafePointer(to: &number) { ptr in
print(ptr.pointee) // 42
}
// 可变指针:
withUnsafeMutablePointer(to: &number) { ptr in
ptr.pointee = 100
}
print(number) // 100
// C 函数互操作:
// 数组传 C 指针:
var arr = [1, 2, 3]
arr.withUnsafeBufferPointer { buf in
// buf.baseAddress 给 C
}
// 注意:
// 指针生命周期必须手动保证
// 不要保存指针逃出作用域
// 多数场景避免使用
// 性能瓶颈才考虑

内存布局

类型内存布局。字节对齐。影响性能与互操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Foundation
// 查看类型大小:
print(MemoryLayout<Int>.size) // 8
print(MemoryLayout<Double>.size) // 8
print(MemoryLayout<Bool>.size) // 1
// 对齐:
print(MemoryLayout<Int>.alignment)
// 结构体布局:
struct Small {
let a: Int8 // 1 字节
let b: Int64 // 8 字节
}
print(MemoryLayout<Small>.size) // 含填充
// 紧凑布局:
// 字段顺序影响填充
// 大字段在前更紧凑
// 优化:
// 避免大量小结构体
// 数组连续存储
// 与 C 互操作关心布局
// 很少需要手动优化

5.流程控制

if、switch、guard、循环与可选绑定。

if / else

if 条件执行。else if 多分支。条件无需括号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let score = 85
// 基本 if:
if score >= 60 {
print("及格")
}
// else if:
if score >= 90 {
print("优秀")
} else if score >= 60 {
print("及格")
} else {
print("不及格")
}
// 条件必须 Bool:
// if score { } // 错误
// 多条件:
if score >= 60 && score < 90 {
print("良好")
}
// 一行 if:
if score > 80 { print("高分") }
// 三目运算符:
let label = score >= 60 ? "过" : "不过"

switch

switch 穷尽匹配。区间、元组、绑定。不需要 break。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let value = 5
// 值匹配:
switch value {
case 1, 2, 3:
print("小")
case 4...6:
print("中")
default:
print("大")
}
// 区间与 where:
let temp = 25
switch temp {
case 0...10: print("冷")
case 11...30 where temp >= 20: print("暖")
case 11...30: print("温")
default: print("热")
}
// 枚举穷尽:
enum Grade { case a, b, c }
let g = Grade.a
switch g {
case .a: print("优秀")
case .b: print("良好")
case .c: print("及格")
}
// 元组匹配:
// switch (x, y) { case (0, 0): ... }

guard

guard 提前退出。else 必须退出作用域。else 有 else 分支。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// guard 提前返回:
func validate(_ age: Int?) {
guard let age = age else {
print("年龄为空")
return
}
// 这里 age 已解包
guard age >= 18 else {
print("未成年")
return
}
print("成人,年龄 \(age)")
}
validate(nil)
validate(15)
validate(20)
// 与 if let 区别:
// guard 的解包值后续可用
// if let 只在分支内可用
// guard 的 else 必须退出:
// return/throw/break/continue
// 结构更清晰、缩进更浅
// 主流程逻辑保持在顶层

for-in 循环

for-in 遍历范围、数组、字典。带索引遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 范围:
for i in 1...5 { // 含 5
print(i)
}
for i in 1..<5 { // 不含 5
print(i)
}
// 数组:
let names = ["a", "b", "c"]
for name in names {
print(name)
}
// 带索引:
for (i, name) in names.enumerated() {
print("\(i): \(name)")
}
// 字典:
let scores = ["a": 90, "b": 80]
for (key, value) in scores {
print("\(key): \(value)")
}
// 倒序:
for i in (1...3).reversed() {}
// 步长:
for i in stride(from: 0, to: 10, by: 2) {}
// 索引数组:
for i in names.indices { print(names[i]) }

while 循环

while 条件循环。repeat-while 先执行后判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var n = 3
// while:
while n > 0 {
print(n)
n -= 1
}
// repeat-while(至少执行一次):
var count = 0
repeat {
count += 1
print("第 \(count) 次")
} while count < 3
// 退出循环:
var found = false
var i = 0
while !found && i < 10 {
i += 1
if i == 5 { found = true }
}
// 用途:
// 条件未知的循环
// 轮询、重试逻辑
// 与 for-in 区别:
// 不需要已知次数时用 while
// repeat-while 适合菜单交互

break 与 continue

break 退出循环。continue 跳过本轮。带标签退出嵌套。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// continue:
for i in 1...10 {
if i % 2 == 0 {
continue // 跳过偶数
}
print(i) // 1 3 5 7 9
}
// break:
for i in 1...10 {
if i > 5 {
break // 提前退出
}
print(i)
}
// 标签退出嵌套:
outer: for i in 1...3 {
for j in 1...3 {
if i * j == 6 {
print("命中 \(i) \(j)")
break outer // 退出全部
}
}
}
// 标签命名任意
// 用于复杂嵌套控制流
// 适度使用,过度降低可读性

if let

可选绑定解包。guard let 提前。多值同时解包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var name: String? = "Nick"
var age: Int? = 30
// 单个解包:
if let name = name {
print("名字 \(name)")
}
// 多个解包(逗号):
if let name = name, let age = age {
print("\(name) \(age) 岁")
}
// 带条件:
if let name = name, name.count > 2 {
print(name)
}
// while 中解包:
var nums = [1, 2, 3]
while let n = nums.popLast() {
print(n) // 3 2 1
}
// 不关心值只判空:
if name != nil { print("有值") }
// 注意:
// if let 分支外不可用
// 需要后续使用用 guard let

模式匹配

switch/if 中的模式。case let、where。类型匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 关联值绑定:
enum Result3 {
case success(Int)
case failure(String)
}
let r = Result3.success(200)
switch r {
case .success(let code):
print("成功 \(code)")
case .failure(let msg):
print("失败 \(msg)")
}
// 区间匹配:
let grade = 85
switch grade {
case 90...100: print("A")
case 80..<90: print("B")
case 70..<80: print("C")
default: print("D")
}
// 类型匹配:
let any: Any = 42
switch any {
case let i as Int: print("整数 \(i)")
case let s as String: print("字符串 \(s)")
default: print("其他")
}
// where 条件:
switch grade {
case let g where g % 5 == 0: print("整五分")
default: print("其他分")
}

6.函数

函数定义、参数标签、返回值、函数类型与闭包。

函数定义

func 定义函数。参数与返回值。调用语法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 基本函数:
func greet(name: String) -> String {
"你好,\(name)!"
}
// 无返回值:
func sayHello() {
print("hello")
}
// 无返回也可标 Void:
func log(_ msg: String) -> Void {}
// 调用:
let g = greet(name: "Nick")
print(g)
sayHello()
// 多参数:
func add(a: Int, b: Int) -> Int {
a + b
}
// 参数默认标签=参数名
// 调用需写标签
// 最后一个表达式是返回值

参数标签

参数标签与内部名。_ 省略标签。可读性设计。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 自定义标签:
func move(from start: Int, to end: Int) {
print("从 \(start)\(end)")
}
move(from: 1, to: 10)
// 省略标签:
func sum(_ a: Int, _ b: Int) -> Int {
a + b
}
print(sum(1, 2))
// 混合:
func configure(_ name: String, debug: Bool = false) {
print(name, debug)
}
configure("app") // 省略标签
configure("app", debug: true)
// API 设计规范:
// 标签使调用读作句子
// 介词 with/for/in 常见
// 标签用词清晰避免歧义

默认参数值

参数默认值。调用时可省略。必须尾部或都有默认。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 默认值:
func greet2(_ name: String, times: Int = 1) {
for _ in 1...times {
print("你好 \(name)")
}
}
greet2("Nick") // 用默认 1
greet2("Nick", times: 3)
// 默认值常见用法:
func fetch(
url: String,
timeout: Double = 30,
retries: Int = 3
) {
print(url, timeout, retries)
}
// 调用任意组合:
fetch(url: "https://x")
fetch(url: "https://x", retries: 5)
// 规则:
// 默认值在参数列表最后
// 默认参数可不传
// 默认值在函数签名中体现
// 可读性与灵活性的平衡

返回值

返回值类型。多值用元组。隐式返回单表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 单一返回值:
func square(_ n: Int) -> Int {
n * n
}
// 元组多值返回:
func stats(_ nums: [Int]) -> (min: Int, max: Int, sum: Int) {
(nums.min() ?? 0, nums.max() ?? 0, nums.reduce(0, +))
}
let s = stats([3, 1, 4, 1])
print(s.min, s.max, s.sum)
// 可选值返回:
func findIndex(of value: Int, in arr: [Int]) -> Int? {
arr.firstIndex(of: value)
}
if let i = findIndex(of: 4, in: [1, 2, 4]) {
print(i) // 2
}
// 无返回值:
func noReturn() {}
// 隐式返回:
// 单表达式省略 return
// 多语句需显式 return

可变参数

... 收集多个参数为数组。任意数量。最后一个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 可变参数:
func average(_ numbers: Double...) -> Double {
let sum = numbers.reduce(0, +)
return numbers.isEmpty ? 0 : sum / Double(numbers.count)
}
print(average(1, 2, 3, 4)) // 2.5
print(average()) // 0
// 可变参数作为数组使用
// 打印任意数量:
func logAll(_ items: String...) {
for item in items {
print(item)
}
}
logAll("a", "b", "c")
// 规则:
// 可变参数放最后
// 一个函数最多一个
// 内部是 [T] 数组
// 调用时逗号分隔

inout 参数

inout 修改外部变量。& 传址。值类型原地修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 交换:
func swapTwo<T>(_ a: inout T, _ b: inout T) {
let tmp = a
a = b
b = tmp
}
var x = 5
var y = 10
swapTwo(&x, &y)
print(x, y) // 10 5
// 值类型修改:
func scale(_ v: inout Double, by factor: Double) {
v *= factor
}
var price = 100.0
scale(&price, by: 0.8)
print(price) // 80.0
// 规则:
// 传变量而非常量/字面量
// & 前缀
// 与引用类型区别:
// inout 是函数内外的值往返
// 不创建新实例
// 适合大值类型避免拷贝

函数类型

函数可作为类型。赋值、传参、返回。一等公民。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 函数类型标注:
func add(a: Int, b: Int) -> Int { a + b }
// (Int, Int) -> Int
// 赋值:
let operation: (Int, Int) -> Int = add
print(operation(1, 2))
// 作为参数:
func apply(_ f: (Int) -> Int, to value: Int) -> Int {
f(value)
}
func double(_ n: Int) -> Int { n * 2 }
print(apply(double, to: 5)) // 10
// 返回函数:
func makeAdder(_ base: Int) -> (Int) -> Int {
{ n in base + n }
}
let addTen = makeAdder(10)
print(addTen(5)) // 15
// 可选函数类型:
var handler: (() -> Void)?
// 调用需处理可选
// 类型匹配严格(含标签)

闭包

闭包捕获上下文。尾随闭包。简写参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 定义闭包:
let square2 = { (n: Int) -> Int in
n * n
}
print(square2(4))
// 捕获变量:
var counter = 0
let increment = {
counter += 1
}
increment()
increment()
print(counter) // 2
// 尾随闭包:
func perform(_ action: () -> Void) {
action()
}
perform {
print("尾随闭包")
}
// 排序简写:
let nums = [3, 1, 2]
let sorted = nums.sorted { $0 < $1 }
print(sorted)
// 简写:
// $0 $1 参数简写
// 捕获列表 [weak self]
// @escaping 逃逸闭包

7.字符串

String 操作、插值、子串、Unicode 与格式化。

String 基础

String 值类型。字面量与可变性。Character 组成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 创建字符串:
let greeting = "Hello"
// 多行字符串:三引号字面量
// let multi = """
// 多行
// 字符串
// """
// 等价(\n 换行):
let multi = "多行\n字符串"
// 可变字符串:
var text = "start"
text += " end"
// 空字符串:
let empty = ""
let empty2 = String()
// 字符序列:
// String 由 Character 组成
// 遍历字符:
for c in text {
print(c)
}
// 计数(字符数):
print(text.count)
// 判断空:
text.isEmpty
// 字符串是值类型:
// 赋值拷贝
// 高效(写时复制)

插值

\(表达式) 嵌入值。类型安全。任意表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let name = "Nick"
let age = 30
let score = 95.5
// 基础插值:
let msg = "\(name) 今年 \(age) 岁"
print(msg)
// 表达式插值:
let calc = "\(age * 2)\(age) 的两倍"
// 调用方法:
let upper = "大写: \(name.uppercased())"
// 格式化值:
let formatted = "分数: \(String(format: "%.1f", score))"
// 嵌套插值:
let nested = "\(name)\("\(age)".count) 个字符"
// 转义反斜杠:
let backslash = "反斜杠:\\"
// 注意:
// 插值类型需可显示
// 自定义类型实现 CustomStringConvertible

拼接

字符串拼接。append。join。+= 操作符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// + 拼接:
let a = "hello"
let b = " world"
let c = a + b // 新字符串
// += 追加:
var s = "start"
s += " - more"
// append:
s.append("!")
// 字符追加:
s.append(Character("?"))
// 数组 join:
let parts = ["a", "b", "c"]
let joined = parts.joined(separator: ", ")
print(joined) // "a, b, c"
// 性能:
// 小字符串拼接开销小
// 大量拼接:
// 用数组 + joined
// 或可变字符串一次构建
// 字符串是值类型,拼接创建新值

多行字符串

三引号多行。缩进裁剪。内插值。 分隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 多行字符串用三引号字面量:
// let poem = """
// 床前明月光,
// 疑是地上霜。
// """
// 等价实现(\n 换行):
let poem = "床前明月光,\n疑是地上霜。"
print(poem)
// 缩进规则:以闭合引号缩进为基准
// 包含引号:
// let quote = """
// He said "hi"
// """
// 多行插值:
let name = "Rust"
// let info = """
// 语言:\(name)
// 类型:静态
// """
// 反斜杠取消换行:
// let oneLine = """
// 一行内容 \
// 继续
// """

常用方法

大小写、查找、替换、分割、修剪。常用字符串 API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let s = " Hello, Swift "
// 大小写:
let lower = s.lowercased()
let upper = s.uppercased()
// 修剪:
let trimmed = s.trimmingCharacters(
in: .whitespacesAndNewlines)
// 判断:
s.contains("Swift")
s.hasPrefix(" ")
s.hasSuffix(" ")
// 查找:
s.firstIndex(of: ",")
// 替换:
let replaced = s.replacingOccurrences(
of: "Swift", with: "Go")
// 分割:
let parts = "a,b,c".split(separator: ",")
// 前缀/后缀:
let pre = s.prefix(5)
// 判断空:s.isEmpty
// 重复:
String(repeating: "ab", count: 3)

子串

Substring 引用原串。切片返回 Substring。转 String 持有。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let full = "Hello, Swift World"
// 索引范围切片:
let start = full.index(full.startIndex, offsetBy: 7)
let end = full.index(start, offsetBy: 5)
let sub = full[start..<end] // "Swift"
print(sub)
// 注意:Substring 类型
// 与原字符串共享存储
// 转 String 独立持有:
let owned = String(sub)
// 前缀后缀是 Substring:
let p = full.prefix(5) // "Hello"
// 用 Range 切:
if let range = full.range(of: "Swift") {
print(full[range])
}
// 多字节安全:
// 索引按字符边界
// 不要用整数下标
// 频繁用需转 String

Unicode

字符由 Unicode 标量组成。码点。扩展字位簇。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Unicode 字面量:
let heart = "\u{2764}"
print(heart) // ❤
// 扩展字位簇:
let e = "\u{E9}" // é
let combo = "e\u{301}" // é(组合)
print(e == combo) // true,同字位簇
// 字符(字位簇)计数:
let flag = "🇨🇳"
print(flag.count) // 1(区域指示符组合)
// 遍历字位簇:
for c in "你好👍" {
print(c)
}
// 码点:
for scalar in "中".unicodeScalars {
print(scalar.value) // 20013
}
// 规范化:
// .precomposedStringWithCanonicalMapping()
// 排序比较需规范化

格式化

String(format:) C 风格格式。数字填充与精度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Foundation
// 基础格式:
let pi = 3.14159
let f = String(format: "%.2f", pi)
print(f) // "3.14"
// 整数补零:
String(format: "%05d", 42) // "00042"
// 宽度对齐:
String(format: "%10s", "hi") // 右对齐
// 科学计数:
String(format: "%e", 1234.0)
// 十六进制:
String(format: "%x", 255) // "ff"
// 多占位:
String(format: "%d-%@", 2026, "08-02")
// 注意:
// %@ 对象、%d 整数、%f 浮点
// %lld 64 位整数
// 更 Swift 的方式:
// 优先用插值与描述
// 需要精确格式才用 format

8.集合

Array、Dictionary、Set、高阶函数与区间。

Array

有序值集合。泛型 [T]。增删改查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 创建:
var nums = [1, 2, 3]
let empty: [Int] = []
let filled = [Int](repeating: 0, count: 5)
// 字面量类型推断:
let names = ["a", "b"]
// 访问:
print(nums[0])
print(nums.first ?? 0)
print(nums.last ?? 0)
// 增删:
nums.append(4)
nums.insert(0, at: 0)
nums.removeLast()
nums.remove(at: 1)
// 遍历:
for n in nums { print(n) }
// 其他:
nums.count
nums.isEmpty
nums.contains(2)
// 求和:
let sum = nums.reduce(0, +)
// 排序:
nums.sorted()

数组操作

切片、替换、合并、查找。常用高阶方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var arr = [1, 2, 3, 4, 5]
// 切片:
let slice = arr[1...3] // ArraySlice
print(slice)
// 范围替换:
arr.replaceSubrange(0..<2, with: [9, 9])
// 合并:
let combined = arr + [10]
// 查找:
arr.first { $0 > 3 }
arr.last { $0 > 3 }
arr.contains(5)
arr.firstIndex(of: 3)
// 批量转换:
let doubled = arr.map { $0 * 2 }
let evens = arr.filter { $0 % 2 == 0 }
// 分区:
let (small, big) = arr.partitioned {
$0 < 3
}
// 前缀后缀:
arr.prefix(2)
arr.suffix(2)
// 交换:
arr.swapAt(0, 1)

Dictionary

键值对集合。[String: T]。无序。O(1) 查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 创建:
var scores: [String: Int] = [:]
var config = ["env": "dev", "port": "8080"]
// 增改:
scores["alice"] = 90
scores["bob"] = 85
scores["alice"] = 95 // 覆盖
// 读取(可选):
let alice = scores["alice"] // Int?
let missing = scores["x"] // nil
// 默认值:
let v = scores["x"] ?? 0
// 删除:
scores["bob"] = nil
scores.removeValue(forKey: "alice")
// 遍历:
for (k, v) in scores {
print("\(k): \(v)")
}
// 键列表/值列表:
Array(scores.keys)
Array(scores.values)
// 合并:
config.merge(["port": "9090"]) { _, new in new }
// 计数与空:
scores.count

Set

无序唯一元素集合。哈希集合。集合运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 创建:
var set: Set<Int> = [1, 2, 3]
let other: Set = [3, 4, 5]
// 增删查:
set.insert(4)
set.remove(1)
set.contains(2)
// 集合运算:
let union = set.union(other) // 并
let intersect = set.intersection(other) // 交
let diff = set.subtracting(other) // 差
let sym = set.symmetricDifference(other) // 对称差
// 判断关系:
set.isSubset(of: other)
set.isSuperset(of: other)
set.isDisjoint(with: other)
// 遍历:
for v in set { print(v) }
// 转数组:
let arr = Array(set)
// 用途:
// 去重、存在性检查
// 去重:
let dup = Array(Set([1, 1, 2, 3]))
print(dup)

遍历

数组/字典/集合遍历。带索引。倒序。过滤条件遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let items = ["a", "b", "c"]
// 基础遍历:
for item in items {
print(item)
}
// 带索引:
for (i, item) in items.enumerated() {
print("\(i): \(item)")
}
// 下标遍历:
for i in items.indices {
print(items[i])
}
// 倒序:
for item in items.reversed() {}
// 字典遍历:
let dict = ["a": 1, "b": 2]
for (k, v) in dict {
print("\(k): \(v)")
}
// 条件过滤遍历:
for n in 1...10 where n % 2 == 0 {
print(n) // 2 4 6 8 10
}
// 跳过与退出:
// continue / break
// forEach:
items.forEach { print($0) }

高阶函数

map、filter、reduce、compactMap、sorted。函数式处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let nums = [1, 2, 3, 4, 5]
// map 映射:
let doubled = nums.map { $0 * 2 }
// filter 过滤:
let evens = nums.filter { $0 % 2 == 0 }
// reduce 归约:
let sum = nums.reduce(0, +)
// compactMap 去 nil:
let strs = ["1", "a", "3"]
let ints = strs.compactMap { Int($0) }
print(ints) // [1, 3]
// flatMap 展平:
let grid = [[1, 2], [3]]
let flat = grid.flatMap { $0 }
// sorted 排序:
let sorted = nums.sorted { $0 > $1 }
// 链式组合:
let result = nums
.filter { $0 % 2 == 0 }
.map { $0 * $0 }
.reduce(0, +)
print(result) // 4 + 16 = 20
// forEach 遍历
// 惰性:lazy.map

嵌套集合

集合的组合。多维数组、字典内数组、结构体集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 二维数组:
var grid = [[Int]]()
grid.append([1, 2, 3])
grid.append([4, 5, 6])
print(grid[0][1]) // 2
// 重复初始化:
var matrix = [[Int]](
repeating: [Int](repeating: 0, count: 3), count: 3)
matrix[1][2] = 9
// 字典内数组:
var groups: [String: [String]] = [:]
groups["dev"].append("nick") // 错误
// 需先初始化:
groups["dev"] = ["nick"]
groups["dev", default: []].append("tom")
// 结构体集合:
struct Task { var title: String; var done = false }
var tasks: [Task] = [Task(title: "a")]
tasks[0].done = true
// 数组字典:
let scores: [[String: Int]] = [
["math": 90], ["eng": 85]
]
print(scores[0]["math"] ?? 0)

Range

半开区间 ..<、闭区间 ...。数组切片与循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 范围类型:
let closed = 1...5 // 含 5
let halfOpen = 1..<5 // 不含 5
// 循环:
for i in 1...3 {
print(i)
}
// 数组切片:
let arr = [10, 20, 30, 40]
let sub = arr[1...2] // [20, 30]
// 单侧范围:
let head = arr[..<2] // [10, 20]
let tail = arr[2...] // [30, 40]
// 检查包含:
(1...5).contains(3) // true
// 步长:
for i in stride(from: 0, to: 10, by: 2) {
print(i) // 0 2 4 6 8
}
// 反转:
for i in (1...3).reversed() {}
// 边界:
// 空范围:1..<1
// 浮点范围(限制多)
// Range 用于下标与模式匹配

9.内存管理

ARC 引用计数、弱引用、循环引用与内存优化。

ARC

自动引用计数管理类实例。引用计数归零释放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Device {
var name = ""
deinit {
print("\(name) 释放")
}
}
// 引用计数:
var d1: Device? = Device() // count 1
d1?.name = "phone"
var d2 = d1 // count 2
var d3 = d1 // count 3
d1 = nil // count 2
d2 = nil // count 1
d3 = nil // count 0,释放
// 规则:
// 每强引用 +1
// 计数归零自动释放
// deinit 可观察释放
// 值类型(struct)不参与 ARC
// 无需手动管理
// 弱引用不计数

weak/unowned

弱引用不增计数。防止循环引用。访问已释放处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Parent {
var child: Child?
}
class Child {
// weak 不持有父:
weak var parent: Parent?
}
var parent: Parent? = Parent()
var child: Child? = Child()
parent?.child = child
child?.parent = parent
// 循环已打破:
parent = nil
// child 的 parent 自动变 nil
// weak 特性:
// 必须 var、必须可选
// 对象释放自动变 nil
// unowned:
// class CreditCard {
// unowned let owner: Person
// }
// unowned 假定一直存在
// 访问已释放 unowned 崩溃
// 优先级:weak > unowned

自动释放池

autoreleasepool 延迟释放。大循环内存控制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 大批量循环:
var results: [String] = []
// 循环外包池:
for i in 0..<1000 {
autoreleasepool {
// 临时对象在此释放:
let temp = "临时数据 \(i)"
results.append(temp)
// 其他临时对象...
}
}
// 每轮迭代结束释放
// 用途:
// 大循环峰值内存
// 处理大量图片/文件
// 耗时任务临时对象
// 注意:
// 现代 Swift 多自动管理
// 需要手动时才用
// 池内 return 提前释放
// ARC 下多数情况无需

拷贝语义

值类型拷贝安全。大类型优化。共享与唯一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 值类型拷贝:
var arr1 = [1, 2, 3]
var arr2 = arr1 // 写时复制
// 修改触发真拷贝:
arr2.append(4)
print(arr1) // [1, 2, 3] 不变
// 大字符串/数组/字典:
// 赋值廉价(共享存储)
// 修改才分配
// 自定义 struct 同样安全:
struct Config {
var theme = "dark"
var font = 14
}
var c1 = Config()
var c2 = c1
c2.theme = "light"
print(c1.theme) // dark
// 拷贝原则:
// 值语义防意外共享
// 线程安全(无共享状态)
// 需要共享用 class

循环引用

两个类互相强引用形成环。内存泄漏。weak 打破。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Owner {
var pet: Pet?
deinit { print("Owner 释放") }
}
class Pet {
// BAD:强引用父,形成环
// var owner: Owner?
// GOOD:weak 弱引用
weak var owner: Owner?
deinit { print("Pet 释放") }
}
var owner: Owner? = Owner()
var pet: Pet? = Pet()
owner?.pet = pet
pet?.owner = owner
// 无环时正常释放:
owner = nil
pet = nil
// 诊断:
// Instruments 泄漏检查
// deinit 未调用 = 泄漏
// 其他循环场景:
// 闭包捕获 self 强引用
// 用 [weak self] 打破
// Timer/Delegate 注意

栈与堆

值类型栈上、引用类型堆上。分配与性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 栈上分配:
struct Point { var x = 0 }
let p = Point() // 栈上(多数情况)
// 堆上分配:
class Box { var v = 0 }
let b = Box() // 堆上
// 大结构体优化:
// 超过一定大小也堆上
// 数组元素内联存储
// 性能影响:
// 栈分配快、自动释放
// 堆分配需 ARC 管理
// 大量小对象
// 用 struct 优于 class
// 结构体数组连续内存:
struct Item { var id = 0 }
let items = [Item(), Item()] // 连续
// 参考:
// 值类型默认栈
// 逃逸才上堆
// 无需过度关注

lazy 存储

lazy 延迟初始化。用到才创建。耗时属性优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// lazy 属性:
class DataLoader {
// 用到才加载:
lazy var config: [String: String] = {
print("加载配置")
return ["env": "dev"]
}()
}
let loader = DataLoader()
// 未访问不执行:
print("已创建")
// 首次访问触发:
print(loader.config)
// 后续直接返回:
print(loader.config)
// 特点:
// 只初始化一次
// 线程不安全(并发需注意)
// 闭包内可用 self
// 适合:
// 昂贵构建
// 可能不使用的属性
// 依赖其他属性的初始化
// 全局变量也 lazy

内存优化

减少堆分配、复用缓冲、避免大拷贝。性能调优。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 减少临时分配:
// 复用可变字符串:
var buffer = ""
for i in 0..<1000 {
// 追加而非拼接:
buffer.append("\(i),")
}
// 预分配容量:
var nums: [Int] = []
nums.reserveCapacity(1000)
for i in 0..<1000 {
nums.append(i)
}
// 避免不必要拷贝:
// 传 inout 修改大数组
// 用 ArraySlice 共享
// 值类型注意
// 大结构体传引用
// 用 withUnsafeBytes
// 检查工具:
// Instruments Allocations
// 时间分析优先
// 别过早优化

10.类与协议

class、继承、协议、扩展与面向协议设计。

class 定义

类定义属性与方法。引用类型。初始化器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Animal {
var name: String
// 初始化器:
init(name: String) {
self.name = name
}
// 方法:
func speak() {
print("\(name) 叫了一声")
}
}
// 实例化:
let dog = Animal(name: "旺财")
dog.speak()
// 引用语义:
let same = dog
same.name = "小黑"
print(dog.name) // 小黑
// 属性默认值:
class Counter {
var count = 0
init() {}
}
// deinit 析构:
deinit {
print("实例释放")
}
// 类可继承、可扩展
// 与 struct 对比
// 类需要 init 保证所有存储属性

继承

子类继承父类。存储属性、方法。最终类 final。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Vehicle {
var speed = 0
func describe() -> String {
"速度 \(speed)"
}
}
// 子类:
class Car: Vehicle {
var wheels = 4
override func describe() -> String {
"汽车 \(wheels) 轮,\(super.describe())"
}
}
let car = Car()
car.speed = 100
print(car.describe())
// 继承规则:
// 单继承
// override 覆盖必须标注
// 访问父类用 super
// 不能继承的:
// final class 不可继承
// 私有属性不可见
// 指定/便捷初始化器
// 优先组合与协议

方法重写

override 覆盖方法、属性、初始化器。super 调用父类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Base {
var label = "base"
func greet() {
print("Base: \(label)")
}
// 可重写属性:
var value: Int {
get { 10 }
}
}
class Sub: Base {
// 重写方法:
override func greet() {
super.greet() // 先调父类
print("Sub 额外处理")
}
// 重写属性:
override var value: Int {
get { super.value + 5 }
}
}
let s = Sub()
s.greet()
print(s.value)
// 规则:
// override 必写
// 可读性:
// final 阻止重写
// final func 防再覆写
// 属性重写用 get/set/willSet/didSet
// 静态方法可重写

协议

protocol 定义要求。类型遵守。面向协议设计核心。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 定义协议:
protocol Greetable {
var name: String { get }
func greet() -> String
}
// 遵守协议:
struct Person: Greetable {
var name: String
func greet() -> String {
"你好,\(name)!"
}
}
struct Robot: Greetable {
var name: String
func greet() -> String {
"哔,\(name) 就绪"
}
}
// 协议类型使用:
let things: [Greetable] = [
Person(name: "Nick"),
Robot(name: "R2"),
]
for t in things {
print(t.greet())
}
// 协议可继承:
protocol NamedGreetable: Greetable {
var age: Int { get }
}
// 协议默认实现用扩展

协议扩展

extension 提供默认实现。约束扩展。协议默认方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 协议:
protocol Describable {
var description: String { get }
}
// 默认实现:
extension Describable {
var description: String {
"描述:" + Self.self.description
}
}
// 约束扩展:
extension Describable where Self: Equatable {
func isSameAs(_ other: Self) -> Bool {
self == other
}
}
// 为类型添加协议:
struct Cat: Describable {
var name: String
}
// 使用默认实现:
let cat = Cat(name: "喵")
print(cat.description)
// 协议 + 扩展:
// 默认方法减少样板
// 协议继承组合
// where 约束细化
// 面向协议编程的基础

扩展

extension 给现有类型加方法。分类组织代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 扩展内置类型:
extension String {
// 加方法:
func isEmail() -> Bool {
self.contains("@")
}
// 计算属性:
var wordCount: Int {
self.split(separator: " ").count
}
}
print("[email protected]".isEmail())
print("hi there".wordCount)
// 扩展自己的类型:
struct Point { var x: Double }
extension Point {
func doubled() -> Point {
Point(x: x * 2)
}
}
// 遵守协议用扩展:
extension Point: Equatable {}
// 分组组织:
// 一个类型多个 extension
// 按功能分类
// 不能加存储属性
// 不能改既有实现

属性观察器

willSet/didSet 监听属性变化。界面更新、校验。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var user: String = "" {
willSet {
print("即将改为 \(newValue)")
}
didSet {
print("已从 \(oldValue) 改为 \(user)")
}
}
user = "Nick"
user = "Tom"
// 输出观察日志
// 应用场景:
// 界面绑定刷新
// 数据校验
// 记录变更
// 规则:
// 初始化不触发
// 类与 struct 都可
// 存储属性才能观察
// 计算属性用 get/set
// willSet/didSet 参数可命名
// 并发访问注意线程

计算属性

get/set 计算值。不存储。派生数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct Circle {
var radius: Double
// 计算属性:
var area: Double {
// 只读:
Double.pi * radius * radius
}
// 读写:
var diameter: Double {
get {
radius * 2
}
set {
radius = newValue / 2
}
}
}
var c = Circle(radius: 5)
print(c.area) // 78.54
print(c.diameter) // 10
c.diameter = 20
print(c.radius) // 10
// 特点:
// 每次访问重新计算
// 不占存储
// 派生值始终一致
// get 必须有
// set 用 newValue
// 也可自定义参数名
// 与存储属性对比

11.错误处理

throws、do-catch、try、defer 与自定义错误。

Error 协议

Error 协议标记错误类型。枚举定义错误分类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 错误枚举:
enum FileError: Error {
case notFound
case noPermission
case invalidData(String)
}
// 简单错误:
struct SimpleError: Error {
let message: String
}
// 分类错误:
enum LoginError: Error {
case emptyUsername
case wrongPassword
case tooManyAttempts
}
// 用途:
// 标记可抛错误
// 携带信息:
// case invalidData(String)
// 错误符合可比较:
// 可加 Equatable 派生
// LocalizedError 提供描述
// 错误设计清晰分类

throws

throws 标记可抛函数。throw 抛出错误。调用需处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 抛错函数:
func divide(_ a: Int, by b: Int) throws -> Int {
guard b != 0 else {
throw DivideError.zeroDivision
}
return a / b
}
enum DivideError: Error {
case zeroDivision
}
// 调用必须处理:
// 1. try! 危险
// let x = try! divide(1, by: 0)
// 2. try? 转可选
let x = try? divide(1, by: 0)
print(x) // nil
// 3. try + do-catch
// 4. 传播给调用者
func safeDivide() throws -> Int {
try divide(10, by: 2)
}
// 链式 throw:
// 函数标记 throws 即可透传

do-catch

do 块中 try。catch 捕获错误。多分支。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum CalcError: Error {
case zeroDiv
case overflow
}
func compute() throws -> Int {
throw CalcError.zeroDiv
}
// 捕获:
do {
let result = try compute()
print(result)
} catch CalcError.zeroDiv {
print("除以零")
} catch CalcError.overflow {
print("溢出")
} catch {
print("其他错误: \(error)")
}
// 绑定错误:
// catch let e as CalcError
// 一个 do 多个 try
// catch 从具体到一般
// error 隐式变量
// 未捕获:
// 传播到调用者
// 没有 catch 需 throws

try 变体

try 需要 catch。try? 转可选。try! 强制(崩溃风险)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func risky() throws -> Int { 42 }
// try:必须 do-catch 或传播
// 语法要求:
func caller() throws {
let v = try risky()
print(v)
}
// try?:失败转 nil
let a = try? risky() // Int?
print(a ?? 0)
// try!:失败崩溃
// 适合确定不失败:
let b = try! risky()
print(b)
// 例子:
// let data = try? Data(contentsOf: url)
// 强制解包的权衡:
// try! 失败直接崩溃
// 生产谨慎使用
// 测试/确定场景可用
// try? 适合“失败也可”场景

自定义错误

错误携带上下文。LocalizedError 描述。错误码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 带上下文:
enum NetworkError: LocalizedError {
case timeout(Int)
case serverError(code: Int, message: String)
// 本地化描述:
var errorDescription: String? {
switch self {
case .timeout(let s):
return "超时 \(s) 秒"
case .serverError(let code, let msg):
return "服务器 \(code): \(msg)"
}
}
}
// 使用:
func fetch() throws {
throw NetworkError.serverError(code: 500, message: "内部错误")
}
do {
try fetch()
} catch let e as NetworkError {
print(e.errorDescription ?? e)
}
// 捕获任意 Error:
// catch let err { print(err.localizedDescription) }
// 关联值提供上下文
// 协议组合:
// Equatable 比较错误

defer 清理

defer 作用域结束时执行。清理资源。逆序执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func processFile() throws {
print("打开文件")
// 延迟清理:
defer {
print("关闭文件")
}
print("处理数据")
// 中途出错也会执行 defer
// throw SomeError()
print("正常结束")
}
try? processFile()
// 多个 defer 逆序:
defer { print("A") }
defer { print("B") }
// 输出 B 后 A
// 用途:
// 关闭文件/连接
// 解锁、恢复状态
// 清理临时资源
// 提前 return 也执行
// 注意:
// 不在 defer 中读写返回值

致命错误

fatalError 不可恢复崩溃。precondition 条件检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 不可恢复:
// fatalError("无法继续")
// precondition:
func process(_ n: Int) {
precondition(n > 0, "参数必须为正数")
print(n)
}
process(5)
// process(-1) // 崩溃(precondition 不满足)
// 调试断言:
assert(true, "仅调试检查")
// 其他:
// assert 调试构建生效
// precondition 都生效
// 适用场景:
// 不变量被破坏
// 程序无法继续的 bug
// 开发期尽早失败
// 生产用错误处理
// 不要滥用

Result 类型

Result<Success, Failure> 显式成功或失败。避免 throws。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Result 使用:
func fetchData() -> Result<[String], Error> {
let ok = true
if ok {
return .success(["a", "b"])
} else {
return .failure(NetworkError.timeout(30))
}
}
enum NetworkError: Error {
case timeout(Int)
}
// 处理:
let result = fetchData()
switch result {
case .success(let data):
print(data)
case .failure(let error):
print(error)
}
// 方法:
// result.map { ... }
// result.flatMap { ... }
// try result.get() 转 throws
// 场景:
// 异步回调返回
// 非异常错误流
// 与 Result<_, Never> 组合
// 存储错误结果数组

12.输入输出

命令行输入、文件读写、FileManager 与 Codable。

读取输入

readLine 读取命令行输入。循环交互。解析数字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Foundation
// 读取一行:
print("请输入名字:", terminator: "")
if let name = readLine() {
print("你好,\(name)!")
}
// 读取数字:
print("输入年龄:", terminator: "")
if let input = readLine(), let age = Int(input) {
print("年龄 \(age)")
}
// 循环交互:
while let line = readLine(), !line.isEmpty {
print("收到: \(line)")
}
// 批量管道输入:
// echo "data" | swift main.swift
// readLine 返回 nil 表示 EOF
// 标准错误输出:
// FileHandle.standardError
// 输出:
// print 到标准输出

读取文件

String(contentsOf:) 读文件。Data 二进制。逐行读取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// 读取为字符串:
let path = "data.txt"
do {
let content = try String(contentsOfFile: path, encoding: .utf8)
print(content)
} catch {
print("读取失败: \(error)")
}
// 读取为 Data:
let data = try? Data(contentsOf: URL(fileURLWithPath: path))
// 逐行处理:
let text = "line1\nline2\n"
for line in text.components(separatedBy: .newlines) {
print(line)
}
// 相对路径注意:
// 当前工作目录
// 沙盒环境需 Document 目录
// 编码:
// .utf8 / .unicode
// 大文件流式读取:
// FileHandle + read(upToCount:)

写入文件

写入字符串与 Data。追加模式。原子写入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Foundation
let content = "hello\nworld\n"
// 覆盖写入:
let url = URL(fileURLWithPath: "out.txt")
try? content.write(to: url, atomically: true, encoding: .utf8)
// Data 写入:
let data = Data(content.utf8)
try? data.write(to: url)
// 追加写入:
if let handle = try? FileHandle(forWritingTo: url) {
handle.seekToEndOfFile()
handle.write(Data("more\n".utf8))
try? handle.close()
}
// 路径处理:
let dir = URL(fileURLWithPath: "data")
try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
// 错误处理:
// try? 忽略错误
// 生产用 do-catch
// 原子写入防损坏

FileManager

文件系统操作。创建目录、删除、移动、判断存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
let fm = FileManager.default
// 判断存在:
let exists = fm.fileExists(atPath: "data.txt")
// 创建目录:
try? fm.createDirectory(
atPath: "data/sub", withIntermediateDirectories: true)
// 移动/重命名:
try? fm.moveItem(atPath: "a.txt", toPath: "b.txt")
// 复制:
try? fm.copyItem(atPath: "b.txt", toPath: "c.txt")
// 删除:
try? fm.removeItem(atPath: "c.txt")
// 列出目录:
if let items = try? fm.contentsOfDirectory(atPath: ".") {
print(items)
}
// 属性:
if let attrs = try? fm.attributesOfItem(atPath: "data.txt") {
print(attrs[.size] ?? "?")
}
// 目录遍历:
// enumerator(atPath:)
// 沙盒路径:
// fm.urls(for: .documentDirectory, in: .userDomainMask)

输出控制

print 变体。stderr。分隔与终止。字符串描述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
// 基础:
print("普通输出")
print(1, 2, 3) // 空格分隔
print("a", "b", separator: "-")
print("x", terminator: "") // 不换行
print() // 换行
// 标准错误:
FileHandle.standardError.write(
Data("错误信息\n".utf8))
// 调试描述:
debugPrint("debug")
// 自定义输出:
struct Item: CustomStringConvertible {
var name: String
var description: String {
"Item(\(name))"
}
}
print(Item(name: "a"))
// CustomDebugStringConvertible:
// 调试更详细
// dump 深度打印:
dump([1, 2, 3])

Data 处理

Data 字节容器。互转字符串。base64 编解码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
// 字符串转 Data:
let str = "hello"
let data = Data(str.utf8)
// Data 转字符串:
let back = String(data: data, encoding: .utf8)
print(back ?? "")
// base64:
let encoded = data.base64EncodedString()
print(encoded) // aGVsbG8=
let decoded = Data(base64Encoded: encoded)
// 字节操作:
var bytes: [UInt8] = [0x68, 0x69]
let d = Data(bytes)
print(Array(d)) // [104, 105]
// 追加:
var buffer = Data()
buffer.append(Data([1, 2]))
buffer.append(Data("a".utf8))
// 十六进制:
let hex = data.map { String(format: "%02x", $0) }.joined()
print(hex) // 68656c6c6f
// 读取子范围:
// data.subdata(in: 0..<2)

JSONSerialization

JSON 转字典/数组。反向序列化。JSONSerialization 类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
// JSON 字符串:
let jsonStr = "{\"name\": \"Nick\", \"age\": 30, \"tags\": [\"dev\"]}"
let jsonData = Data(jsonStr.utf8)
// 解析为 Any:
let obj = try? JSONSerialization.jsonObject(
with: jsonData)
// 转字典:
if let dict = obj as? [String: Any] {
print(dict["name"] ?? "")
let age = dict["age"] as? Int ?? 0
let tags = dict["tags"] as? [String] ?? []
print(age, tags)
}
// 反向序列化:
let payload: [String: Any] = [
"name": "Tom", "age": 25
]
if JSONSerialization.isValidJSONObject(payload) {
let data = try? JSONSerialization.data(withJSONObject: payload)
let str = String(data: data!, encoding: .utf8)
print(str ?? "")
}
// 现代推荐 Codable

Codable

Codable 自动序列化。JSONEncoder/Decoder。协议驱动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 类型遵守 Codable:
struct User: Codable {
var name: String
var age: Int
var tags: [String]
}
// 编码:
let user = User(name: "Nick", age: 30, tags: ["dev"])
let encoder = JSONEncoder()
let data = try? encoder.encode(user)
let json = String(data: data!, encoding: .utf8)
print(json ?? "")
// 解码:
let jsonStr = "{\"name\": \"Tom\", \"age\": 25, \"tags\": []}"
let decoder = JSONDecoder()
if let u = try? decoder.decode(User.self, from: Data(jsonStr.utf8)) {
print(u.name)
}
// 字段映射:
// CodingKeys 重命名
// snake_case:
// decoder.keyDecodingStrategy = .convertFromSnakeCase
// 数组解码:[User].self
// 日期处理需策略

13.常见误区

Swift 新手最容易踩的坑与正确写法。

强制解包

! 解包 nil 崩溃。优先可选绑定与空合运算符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// BAD:强制解包
var name: String? = nil
// print(name!) // 崩溃
// GOOD:可选绑定
if let name = name {
print(name)
}
// GOOD:空合运算符
let display = name ?? "匿名"
print(display)
// GOOD:guard 提前解包
func show(_ v: String?) {
guard let v = v else {
print("无值")
return
}
print(v)
}
// 何时可安全 !:
// 确定非 nil(已赋值)
// 测试/原型
// 其余避免
// try! 同理谨慎
// 崩溃信息难看
// 生产代码克制 !

可选链

?. 链式访问。任一环节 nil 整体 nil。赋值也可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Address { var city = "北京" }
class Person {
var address: Address?
}
let p = Person()
// GOOD:可选链
let city = p.address?.city // String?
print(city ?? "无地址")
// BAD:假定中间非 nil
// p.address!.city // 可能崩溃
// 多层链:
// p.address?.city.count
// 赋值的可选链:
p.address?.city = "上海" // nil 时静默
// 赋值地址:
p.address = Address()
p.address?.city = "上海"
print(p.address?.city ?? "")
// 方法调用可选链:
// p.address?.method()
// 判断链中是否有值:
if let c = p.address?.city {
print(c)
}
// 常见误区:
// 链返回可选类型
// 不会自动解包
// 与强制链 !. 对比

闭包循环引用

闭包捕获 self 强引用形成环。捕获列表 [weak self]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Loader {
var data: String = ""
var completion: (() -> Void)?
func setup() {
// BAD:闭包强捕获 self
// completion = {
// self.data = "loaded"
// }
// GOOD:[weak self]
completion = { [weak self] in
self?.data = "loaded"
// self 释放后自动 nil
}
}
deinit { print("Loader 释放") }
}
// 使用:
var l: Loader? = Loader()
l?.setup()
// 执行闭包或释放:
l = nil // 正常释放
// 其他打破方式:
// [unowned self] 假定存在
// 闭包外强引用后手动 nil
// 常见于:
// 网络回调、Timer、动画

数组越界

索引越界崩溃。安全访问 first/last/prefix。检查边界。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let nums = [1, 2, 3]
// BAD:越界索引
// print(nums[5]) // 崩溃
// GOOD:安全访问
if nums.indices.contains(5) {
print(nums[5])
}
// 安全方法:
let first = nums.first ?? 0
let last = nums.last ?? 0
// 安全取元素扩展:
extension Array {
func safe(_ index: Int) -> Element? {
indices.contains(index) ? self[index] : nil
}
}
print(nums.safe(1) ?? 0) // 2
print(nums.safe(9) ?? 0) // 0
// 空数组注意:
// nums[0] 也崩溃
// 遍历用 indices 或 enumerated
// 动态索引先检查

mutating

struct 方法修改属性需 mutating。值类型不可变副本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct Counter {
var count = 0
// 需要修改自身:
mutating func increment() {
count += 1
}
// 只读方法不需要:
func current() -> Int {
count
}
}
var c = Counter()
c.increment()
c.increment()
print(c.current()) // 2
// BAD:常量实例调 mutating
// let cc = Counter()
// cc.increment() // 错误
// GOOD:var 变量实例
var cc2 = Counter()
cc2.increment()
// 原因:
// mutating 修改 self
// 常量实例不可变
// 规则:
// struct/enum 需要 mutating
// class 不需要
// 下标、协议方法也适用
// 值语义的体现

字符串索引

字符串不能整数下标。字符多字节。用 index 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let s = "你好 Swift"
// BAD:整数下标
// let c = s[0] // 错误
// 字符数不等于字节数
// GOOD:索引 API
let first = s.startIndex
let c = s[first] // 你
print(c)
// 偏移:
let second = s.index(after: first)
print(s[second])
// 指定偏移:
let idx = s.index(s.startIndex, offsetBy: 3)
print(s[idx])
// 字符数组转换:
let chars = Array(s)
print(chars[0]) // 你
// 遍历:
for c in s { print(c) }
// 切片:
// s[..<idx]
// 多字节安全
// String.Index 保证边界

协议与 Self

协议中使用 Self 约束。associatedtype。类型擦除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 协议 Self 约束:
protocol EquatableLike {
func isEqualTo(_ other: Self) -> Bool
}
// 遵守:
struct Box: EquatableLike {
var value: Int
func isEqualTo(_ other: Box) -> Bool {
self.value == other.value
}
}
// BAD:把含 Self 协议当类型
// func compare(a: EquatableLike, b: EquatableLike) // 错误
// GOOD:泛型约束
func compare<T: EquatableLike>(_ a: T, _ b: T) -> Bool {
a.isEqualTo(b)
}
// 关联类型:
// protocol Container {
// associatedtype Item
// }
// 类型擦除:
// AnyEquatable 包装
// 需要动态多态时
// 用泛型或类型擦除

隐式解包

T! 隐式解包可选。使用未初始化崩溃。谨慎使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// BAD:隐式解包访问未赋值崩溃
var name: String! = nil
// 使用直接:
// print(name) // 崩溃(未赋值)
// 赋值后:
name = "Nick"
print(name) // 自动解包
// 用途:
// IBOutlet 连接
// 初始化后必然有值
// 两阶段初始化
// 风险:
// 访问未赋值时崩溃
// 丢失可选安全性
// 不建议新代码使用
// GOOD:普通可选
var safeName: String?
// 解包:
if let n = safeName {
print(n)
}
// 强制场景:
// 确定初始化后再访问
// 或改为 let 常量

14.并发

GCD、async/await、Task 与 actor 并发模型。

GCD 基础

DispatchQueue 串行/并发队列。全局与主队列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
// 主队列:
let main = DispatchQueue.main
// 全局并发队列:
let background = DispatchQueue.global(qos: .background)
// 创建串行队列:
let serial = DispatchQueue(label: "com.app.serial")
// 创建并发队列:
let concurrent = DispatchQueue(
label: "com.app.concurrent", attributes: .concurrent)
// 提交任务:
background.async {
// 后台执行...
print("后台任务")
}
// 同步提交:
// serial.sync { ... }
// 延迟执行:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
print("1 秒后")
}
// QoS 优先级:
// .userInteractive / .userInitiated
// .utility / .background

调度组

DispatchGroup 等待多个任务完成。批量并发汇总。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
let group = DispatchGroup()
let queue = DispatchQueue.global()
// 进入组:
// 多次异步任务
for i in 0..<3 {
group.enter()
queue.async {
print("任务 \(i) 执行")
// 模拟耗时
Thread.sleep(forTimeInterval: 0.2)
group.leave()
}
}
// 全部完成回调:
group.notify(queue: .main) {
print("所有任务完成")
}
// 同步等待:
// group.wait()
// 超时等待:
// group.wait(timeout: .now() + 5)
// 注意:
// enter 与 leave 必须成对
// 多线程安全
// 适合并行下载/批量处理

async/await

现代 Swift 并发语法。异步函数与挂起。结构化并发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Foundation
// 异步函数:
func fetchData() async -> String {
// 模拟网络延迟
try? await Task.sleep(nanoseconds: 1_000_000_000)
return "数据"
}
// 调用:
func load() async {
let data = await fetchData()
print(data)
}
// 异步抛错函数:
func risky() async throws -> Int {
try await Task.sleep(nanoseconds: 100_000_000)
return 42
}
// 调用:
func caller() async {
if let v = try? await risky() {
print(v)
}
}
// 注意:
// 异步函数不可在同步上下文调用
// Task { } 包一层
// 与旧回调风格互转

async let

并发执行多个异步调用。绑定独立子任务。并行等待。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// 三个独立异步任务:
func loadUser() async -> String { "用户" }
func loadPosts() async -> String { "帖子" }
func loadComments() async -> String { "评论" }
// async let 并发:
func loadAll() async -> String {
async let user = loadUser()
async let posts = loadPosts()
async let comments = loadComments()
// 全部并行执行:
let result = await "\(user) \(posts) \(comments)"
return result
}
// 使用:
// 三个任务同时跑
// await 聚合所有结果
// 注意:
// async let 绑定后必须 await
// 依赖关系:
// 有依赖用顺序 await
// 无依赖用 async let
// 适合并行请求独立接口

Task

Task 创建并发单元。继承上下文。取消与优先级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import Foundation
// 创建任务:
func run() async {
let task = Task {
try await Task.sleep(nanoseconds: 500_000_000)
return "完成"
}
let result = await task.value // String
print(result)
}
// 派发到主线程:
// @MainActor func updateUI()
// Task { @MainActor in ... }
// 任务取消:
task.cancel()
// 检查取消:
try Task.checkCancellation()
// 结构化任务组:
await withTaskGroup(of: String.self) { group in
for i in 0..<3 {
group.addTask {
"任务\(i)"
}
}
for await r in group {
print(r)
}
}
// 优先级:Task(priority: .high)

actor

actor 隔离状态。避免数据竞争。异步方法访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import Foundation
// 定义 actor:
actor BankAccount {
private var balance: Double = 0
func deposit(_ amount: Double) {
balance += amount
}
func getBalance() -> Double {
balance
}
}
// 使用(必须 await):
func test() async {
let account = BankAccount()
// 访问隔离状态需 await:
await account.deposit(100)
let balance = await account.getBalance()
print(balance)
}
// 并发安全:
// 编译器保证串行访问
// 避免数据竞争
// 对比:
// 类需自己加锁
// actor 自动隔离
// 注意:
// actor 方法默认 async 化
// 需 await 调用
// 适合共享可变状态

主线程

UI 操作必须在主线程。线程跳转。主线程检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 判断主线程:
if Thread.isMainThread {
print("在主线程")
}
// 回到主线程:
DispatchQueue.main.async {
// UI 更新
print("主线程执行")
}
// 等待主线程执行:
// DispatchQueue.main.sync { }
// 注意死锁:
// 主线程 sync 到自己会死锁
// 现代 Swift:
@MainActor
func updateUI() {
// 隔离到主线程
}
// 从异步上下文调用:
// await MainActor.run { ... }
// 规则:
// UI 更新、动画主线程
// 耗时任务放后台
// 后台回主线程更新

线程安全

可变状态并发访问保护。锁、队列、原子操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import Foundation
// 用串行队列保护:
final class SafeCounter {
private let queue = DispatchQueue(label: "counter")
private var _count = 0
var count: Int {
queue.sync { _count }
}
func increment() {
queue.sync { _count += 1 }
}
}
let counter = SafeCounter()
for _ in 0..<100 {
DispatchQueue.global().async {
counter.increment()
}
}
// 锁:
// NSLock
let lock = NSLock()
lock.lock()
// 临界区...
lock.unlock()
// 原子:
// OSAllocatedUnfairLock
// 现代方案:
// actor 隔离最安全
// 只读常量无需保护
// 优先不可变数据

15.网络

URLSession、HTTP 请求、异步网络与 JSON 交互。

URLSession

URLSession 发起网络请求。配置与委托。会话类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Foundation
// 默认会话:
let session = URLSession.shared
// 自定义配置:
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 30
config.waitsForConnectivity = true
let custom = URLSession(configuration: config)
// 请求:
let url = URL(string: "https://api.example.com")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Accept")
// 会话类型:
// .default / .ephemeral / .background
// ephemeral 不存缓存 Cookie
// background 后台传输
// 数据任务:
let task = session.dataTask(with: request)
// 配合 async/await 使用

GET 请求

发起 GET。async/await 获取数据。处理响应状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Foundation
// 异步 GET:
func fetchContent() async throws -> String {
let url = URL(string: "https://api.example.com/data")!
let (data, response) = try await URLSession.shared.data(from: url)
// 检查状态码:
guard let http = response as? HTTPURLResponse,
http.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return String(data: data, encoding: .utf8) ?? ""
}
// 使用:
func load() async {
do {
let content = try await fetchContent()
print(content)
} catch {
print("请求失败: \(error)")
}
}
// 查询参数:
var comps = URLComponents(string: "https://api.example.com/search")!
comps.queryItems = [URLQueryItem(name: "q", value: "swift")]
// 超时:
// request.timeoutInterval = 10
// 旧回调式:
// dataTask(with:completionHandler:)

POST 请求

发送 JSON body。表单提交。设置 Content-Type。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// POST JSON:
func postJSON() async throws {
let url = URL(string: "https://api.example.com/users")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
// body:
let payload: [String: Any] = ["name": "Nick", "age": 30]
request.httpBody = try JSONSerialization.data(withJSONObject: payload)
let (data, response) = try await URLSession.shared.data(for: request)
// 处理响应...
print(response)
}
// 表单:
var form = URLRequest(url: URL(string: "https://x/login")!)
form.httpMethod = "POST"
form.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
form.httpBody = "user=nick&pwd=123".data(using: .utf8)
// 注意:
// 大 body 用流式
// 响应同样检查状态码
// Codable 直接编码

异步网络

async/await 网络调用。并发请求。取消任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Foundation
// 异步请求 + Codable:
struct User: Codable { var id: Int; var name: String }
func fetchUsers() async throws -> [User] {
let url = URL(string: "https://api.example.com/users")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([User].self, from: data)
}
// 并发多个请求:
func loadAll() async throws -> (User, [String]) {
async let user = fetchUsers()
async let tags: [String] = ["a", "b"]
return (try await user.first!, try await tags)
}
// 任务取消:
// Task 取消时网络请求自动中断
// 响应检查
func fetchWithTimeout() async throws -> Data {
let url = URL(string: "https://api.example.com")!
// 超时:
let config = URLSessionConfiguration.ephemeral
config.timeoutIntervalForRequest = 10
let session = URLSession(configuration: config)
return try await session.data(from: url).0
}
// 错误处理:
// URLError 类型分类

URL 构建

URLComponents 安全构建 URL。查询参数编码。路径组合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Foundation
// 基础 URL:
var comps = URLComponents()
comps.scheme = "https"
comps.host = "api.example.com"
comps.path = "/v1/search"
// 查询参数:
comps.queryItems = [
URLQueryItem(name: "q", value: "swift 并发"),
URLQueryItem(name: "limit", value: "10"),
URLQueryItem(name: "page", value: "2"),
]
// 自动编码:
let url = comps.url!
print(url.absoluteString)
// 解析 URL:
if let c = URLComponents(string: url.absoluteString) {
print(c.host ?? "")
for item in c.queryItems ?? [] {
print("\(item.name)=\(item.value ?? "")")
}
}
// 好处:
// 自动百分号编码
// 中文/特殊字符安全
// 避免字符串拼接错误
// 添加片段:
comps.fragment = "section"

下载文件

下载任务保存文件。进度监听。后台下载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Foundation
// 数据下载:
func downloadImage() async throws -> Data {
let url = URL(string: "https://example.com/img.png")!
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
// 保存到文件:
func save() async throws {
let data = try await downloadImage()
let dir = FileManager.default.temporaryDirectory
let file = dir.appendingPathComponent("img.png")
try data.write(to: file)
print(file.path)
}
// 文件下载任务:
let config = URLSessionConfiguration.background(withIdentifier: "dl")
let session = URLSession(configuration: config)
// 进度监听:
// 委托代理 URLSessionDownloadDelegate
// 下载完成回调
// 断点续传
// background 支持
// 注意:
// 大文件用磁盘写入
// 沙盒目录选择正确
// 内存考虑

网络 JSON

请求 JSON 并用 Codable 解码。日期与策略处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// 模型:
struct Post: Codable {
var id: Int
var title: String
var created: Date
}
// 日期解码策略:
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
// 蛇形命名:
// decoder.keyDecodingStrategy = .convertFromSnakeCase
// 请求:
func fetchPosts() async throws -> [Post] {
let url = URL(string: "https://api.example.com/posts")!
let (data, _) = try await URLSession.shared.data(from: url)
return try decoder.decode([Post].self, from: data)
}
// 编码上传:
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
// 错误处理:
// DecodingError 分类
// .dataCorrupted / .keyNotFound
// 网络层错误分开处理
// 模型验证

上传文件

multipart 表单上传。文件 body。边界分隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// multipart 上传:
func upload(data: Data, filename: String) async throws {
let url = URL(string: "https://api.example.com/upload")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue(
"multipart/form-data; boundary=\(boundary)",
forHTTPHeaderField: "Content-Type")
// 构建 body:
var body = Data()
body.append("--\(boundary)\r\n".data(using: .utf8)!)
let header = "Content-Disposition: form-data; name=\"file\"; filename=\"\(filename)\"\r\n\r\n"
body.append(header.data(using: .utf8)!)
body.append(data)
body.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
request.httpBody = body
let (_, response) = try await URLSession.shared.data(for: request)
print(response)
}
// 注意:
// boundary 唯一
// 大文件流式上传
// 进度用 URLSessionUploadTask
// 服务器响应校验

16.时间与日期

Date、DateFormatter、日历运算与计时器。

Date

Date 表示时间点。时区无关的绝对时刻。创建与比较。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// 当前时间:
let now = Date()
// 时间戳(秒):
let ts = now.timeIntervalSince1970
print(ts)
// 从时间戳创建:
let fromTs = Date(timeIntervalSince1970: 1700000000)
// 偏移创建:
let inOneHour = Date().addingTimeInterval(3600)
// 比较:
let earlier = Date(timeIntervalSince1970: 0)
print(now > earlier) // true
// 时间间隔:
let diff = now.timeIntervalSince(earlier)
print(diff) // 秒
// 排序:
let dates = [now, earlier].sorted()
// 注意:
// Date 与地区无关
// 展示需 DateFormatter
// 运算用 Calendar
// 不要手动加减秒

日期格式化

DateFormatter 显示日期。本地化格式。自定义格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
let now = Date()
let formatter = DateFormatter()
// 本地化:
formatter.locale = Locale(identifier: "zh_CN")
formatter.timeZone = .current
// 预设风格:
formatter.dateStyle = .medium // 2026年8月2日
formatter.timeStyle = .short // 上午10:30
print(formatter.string(from: now))
// 自定义格式:
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(formatter.string(from: now))
// 格式符号:
// yyyy 年 / MM 月 / dd 日
// HH 24时 / mm 分 / ss 秒
// EEEE 星期全名
formatter.dateFormat = "yyyy年MM月dd日 EEEE"
print(formatter.string(from: now))
// 注意:
// 固定格式时设置 locale
// 展示优先预设风格
// 12/24 小时制

解析日期

字符串转 Date。固定格式解析。ISO8601 解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// 自定义格式解析:
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = .current
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
if let date = formatter.date(from: "2026-08-02 10:30:00") {
print(date)
}
// ISO8601 解析:
let iso = ISO8601DateFormatter()
if let d = iso.date(from: "2026-08-02T10:30:00Z") {
print(d)
}
// 松散解析:
formatter.dateFormat = "yyyy/M/d"
// 注意:
// 解析失败返回 nil
// 固定格式用 en_US_POSIX
// 避免歧义
// 输入格式不一致时
// 多个 formatter 尝试
// 服务器 ISO 优先 iso8601

日期组件

DateComponents 提取年月日。日历组件运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
let calendar = Calendar.current
let now = Date()
// 提取组件:
let comps = calendar.dateComponents(
[.year, .month, .day, .weekday], from: now)
print(comps.year ?? 0, comps.month ?? 0, comps.day ?? 0)
print(comps.weekday ?? 0) // 1=周日...7=周六
// 单独提取:
let year = calendar.component(.year, from: now)
// 组件创建日期:
var c = DateComponents()
c.year = 2026
c.month = 8
c.day = 2
let date = calendar.date(from: c)
print(date ?? Date())
// 今天开始:
let start = calendar.startOfDay(for: now)
// 注意:
// 组件用日历语义
// 跨时区安全
// 代替手算日期

日历运算

日期加减天月。下周/下月。Calendar 运算安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
let calendar = Calendar.current
let now = Date()
// 加天数:
let tomorrow = calendar.date(byAdding: .day, value: 1, to: now)!
// 加月:
let nextMonth = calendar.date(byAdding: .month, value: 1, to: now)!
// 加年:
let nextYear = calendar.date(byAdding: .year, value: 1, to: now)!
// 月初:
let monthStart = calendar.date(from:
calendar.dateComponents([.year, .month], from: now))!
// 下一个周一:
let monday = calendar.nextDate(
after: now, matching: DateComponents(weekday: 2),
matchingPolicy: .nextTime)!
// 日期间隔:
let days = calendar.dateComponents(
[.day], from: now, to: tomorrow).day!
print(days) // 1
// 判断同日:
calendar.isDate(now, inSameDayAs: tomorrow)
// 时区:
// calendar.timeZone

时间间隔

测量代码耗时。TimeInterval 秒单位。性能计时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Foundation
// 计时:
let start = Date()
// 执行任务...
Thread.sleep(forTimeInterval: 0.3)
let elapsed = Date().timeIntervalSince(start)
print("\(elapsed) 秒") // ~0.3
// 高精度计时:
let t0 = DispatchTime.now()
// 任务...
let t1 = DispatchTime.now()
let nanos = t1.uptimeNanoseconds - t0.uptimeNanoseconds
print("\(Double(nanos) / 1e6) ms")
// 休眠:
Thread.sleep(forTimeInterval: 0.5)
// 异步休眠:
// try await Task.sleep(nanoseconds: 500_000_000)
// TimeInterval = Double 秒
// 注意:
// 预热忽略首次
// 多次取平均
// 单位统一秒

Timer

定时器重复执行。一次性定时。RunLoop 注意。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 重复定时器:
var count = 0
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
count += 1
print("tick \(count)")
if count >= 3 {
timer.invalidate() // 停止
}
}
// 一次性:
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) {
print("2 秒后执行")
}
// 注意:
// scheduledTimer 加入当前 RunLoop
// 主线程可用
// 后台线程需 run loop
// 无效:
// timer.invalidate()
// 避免强引用:
// 闭包捕获 self 循环
// 用 [weak self]
// 现代替代:
// Task.sleep 循环

日期比较

比较日期先后。判断同一天。排序与验证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Foundation
let calendar = Calendar.current
let now = Date()
let past = Date(timeIntervalSinceNow: -3600)
let future = Date(timeIntervalSinceNow: 3600)
// 直接比较:
print(future > now) // true
// 同一天:
calendar.isDate(now, inSameDayAs: future)
// 今天/昨天/明天:
calendar.isDateInToday(now)
calendar.isDateInYesterday(past)
calendar.isDateInTomorrow(future)
// 区间包含:
let range = past...future
print(range.contains(now)) // true
// 排序:
let dates = [future, past, now].sorted()
// 差值天数:
let daysBetween = calendar.dateComponents(
[.day], from: past, to: future).day ?? 0
print(daysBetween)
// 验证过期:
if let expires = Calendar.current.date(byAdding: .hour, value: 2, to: now),
now > expires {
print("已过期")
}

17.进程与系统

命令行参数、环境变量、进程操作与文件路径。

命令行参数

CommandLine 获取参数。处理参数与选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Foundation
// 获取参数:
let args = CommandLine.arguments
print(args) // ["程序名", "arg1", "arg2"]
// 参数个数:
let count = CommandLine.arguments.count
// 简单解析:
let arguments = CommandLine.arguments
var verbose = false
var inputFile = ""
var index = 1
while index < arguments.count {
switch arguments[index] {
case "-v", "--verbose":
verbose = true
case "-f", "--file":
index += 1
inputFile = arguments[index]
default:
print("未知参数: \(arguments[index])")
}
index += 1
}
print("verbose=\(verbose), file=\(inputFile)")
// 注意:
// 第一个是程序路径
// 参数用 CommandLine.arguments
// 生产可用 ArgumentParser 库

环境变量

读取环境变量。设置与传递。敏感信息注意。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Foundation
// 读取环境变量:
if let home = ProcessInfo.processInfo.environment["HOME"] {
print(home)
}
// 检查存在:
let hasKey = ProcessInfo.processInfo.environment["API_KEY"] != nil
// 全部变量:
for (key, value) in ProcessInfo.processInfo.environment {
print("\(key)=\(value)")
}
// 默认值:
let port = ProcessInfo.processInfo.environment["PORT"] ?? "8080"
print(port)
// 系统信息:
let os = ProcessInfo.processInfo.operatingSystemVersionString
let cores = ProcessInfo.processInfo.activeProcessorCount
print(os, cores)
// 安全注意:
// 密钥放环境变量
// 不提交到代码库
// 敏感值避免打印

Process

Process 启动子进程。执行命令。捕获输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// 运行命令:
func run(_ cmd: String, _ args: [String]) throws -> String {
let process = Process()
process.executableURL = URL(fileURLWithPath: cmd)
process.arguments = args
// 捕获输出:
let pipe = Pipe()
process.standardOutput = pipe
try process.run()
process.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
return String(data: data, encoding: .utf8) ?? ""
}
// 使用:
try print(run("/usr/bin/env", ["date"]))
// 环境变量:
process.environment = ["PATH": "/usr/bin"]
// 工作目录:
process.currentDirectoryURL = URL(fileURLWithPath: "/tmp")
// 退出码:
let status = process.terminationStatus
// 注意:
// 路径要正确
// 长命令用字符串拆分
// 异步用 terminationHandler

路径处理

URL 路径操作。组合、扩展名、文件名。沙盒目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Foundation
let fileURL = URL(fileURLWithPath: "/tmp/data/file.txt")
// 文件名:
let name = fileURL.lastPathComponent // "file.txt"
// 去掉扩展名:
let base = fileURL.deletingPathExtension().lastPathComponent // "file"
// 扩展名:
let ext = fileURL.pathExtension // "txt"
// 父目录:
let dir = fileURL.deletingLastPathComponent
// 拼接:
let newFile = dir.appendingPathComponent("out.txt")
// 追加扩展名:
let md = fileURL.appendingPathExtension("md")
// 沙盒目录:
let docs = FileManager.default.urls(
for: .documentDirectory, in: .userDomainMask)[0]
let caches = FileManager.default.temporaryDirectory
// 判断标准:
// fileURL.hasDirectoryPath
// 相对路径转绝对:
// URL(fileURLWithPath: "a.txt").standardizedFileURL

退出码

程序退出码。exit 与 fatalError。0 成功非 0 失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// 正常退出:
exit(0)
// 失败退出:
// exit(1)
// 带错误信息:
// 输出到 stderr
// FileHandle.standardError.write(
// Data("错误\n".utf8))
// exit(2)
// 常用约定:
// 0 成功
// 1 一般错误
// 2 用法错误
// 127 命令未找到
// 判断子进程状态:
let process = Process()
// process.waitUntilExit()
// let code = process.terminationStatus
// print(code)
// 注意:
// exit 立即终止
// 不执行 defer
// 脚本中可用
// 正常结束不调 exit
// 返回 Int 主函数

信号处理

捕获系统信号。SIGINT 等。优雅退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Foundation
import Darwin
// 信号处理:
signal(SIGINT) { _ in
print("收到 Ctrl-C")
exit(0)
}
// 忽略信号:
// signal(SIGHUP, SIG_IGN)
// 信号列表:
// SIGINT 中断 (2)
// SIGTERM 终止 (15)
// SIGKILL 强杀 (9) 不可捕获
// 优雅退出模式:
var running = true
signal(SIGTERM) { _ in
running = false
}
// while running {
// // 主循环...
// sleep(1)
// }
// 注意:
// 信号处理器要简单
// 用原子变量通信
// DispatchSource 更现代:
// DispatchSource.makeSignalSource
// 异常退出不推荐

工作目录

当前工作目录获取与切换。文件相对路径基准。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// 当前工作目录:
let cwd = FileManager.default.currentDirectoryPath
print(cwd)
// 切换目录:
// FileManager.default.changeCurrentDirectoryPath("/tmp")
// 相对路径解析:
let rel = URL(fileURLWithPath: "data/config.json")
print(rel.path) // 基于当前目录
// 标准化:
let normalized = rel.standardizedFileURL
// 判断路径:
let isAbsolute = rel.isFileURL
// 常用位置:
let home = FileManager.default.homeDirectoryForCurrentUser
let temp = FileManager.default.temporaryDirectory
// 注意:
// 工作目录影响相对路径
// 沙盒环境不同
// GUI 应用注意:
// 工作目录可能是根
// 显式构建绝对路径更稳
// 配置用相对基准

进程文件 I/O

标准输入读取。标准输出写入。管道交互。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
// 读取标准输入:
if let line = readLine() {
print("收到: \(line)")
}
// 循环读取:
// while let line = readLine() {
// print(line)
// }
// 标准输出:
print("输出到 stdout")
// 标准错误:
FileHandle.standardError.write(
Data("错误信息\n".utf8))
// 从文件重定向:
// swift main.swift < input.txt
// 管道输入:
// echo "data" | swift main.swift
// 输出重定向:
// swift main.swift > out.txt 2>&1
// 注意:
// readLine 返回 nil 表示 EOF
// 大输入注意性能
// 二进制用 FileHandle.standardInput

18.正则表达式

正则字面量、NSRegularExpression 与模式匹配。

正则基础

正则语法概念。字符类、量词、分组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Foundation
// 基本语法:
// 字面量:/abc/ 匹配 abc
// 字符类:[abc] [0-9] \\d
// 量词:* + ? {n,m}
// 锚点:^ $ \\b
// 分组:(...) 捕获
// 或:a|b
// Swift 5.7+ 正则字面量:
let pattern = /\\d{4}-\\d{2}-\\d{2}/
let text = "日期 2026-08-02 结束"
if let match = text.firstMatch(of: pattern) {
print(match.0) // 2026-08-02
}
// 常用:
// \\d 数字 \\w 单词 \\s 空白
// \\D \\W \\S 反向
// 贪婪 vs 懒惰:
// a.*b 贪婪 / a.*?b 懒惰
// 转义:\\\\. 匹配点号

正则字面量

Swift 5.7 正则字面量 /.../。类型化匹配。捕获命名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 正则字面量:
let pattern = /h(\\d+)x/
// 类型化捕获:
let text = "h123x h99x"
if let match = text.firstMatch(of: pattern) {
print(match.0) // h123x
print(match.1) // 123 (Substring)
}
// 命名捕获:
let named = /(?<code>\\d{3})-(?<area>\\d{4})/
if let m = "123-4567".firstMatch(of: named) {
print(m.code) // 123
print(m.area) // 4567
}
// 选项:
let caseInsensitive = /abc/ .ignoresCase()
// 查找全部:
for m in text.matches(of: pattern) {
print(m.0)
}
// 注意:
// 类型安全编译期校验
// 与 NSRegularExpression 互转
// 需 macOS 13 / iOS 16+

NSRegularExpression

NSRegularExpression 正则引擎。范围匹配。兼容旧系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// 创建:
let pattern = "\\d+"
let regex = try! NSRegularExpression(pattern: pattern)
// 匹配:
let text = "价格 100 元,折扣 20 元"
let range = NSRange(text.startIndex..., in: text)
// 全部匹配:
let matches = regex.matches(in: text, range: range)
for m in matches {
if let r = Range(m.range, in: text) {
print(text[r])
}
}
// 捕获组:
let groupRegex = try! NSRegularExpression(pattern: "(\\d+)-(\\d+)")
// 首匹配:
if let first = groupRegex.firstMatch(in: text, range: range) {
// first.range(at: 1) 第一组
}
// 替换:
let replaced = regex.stringByReplacingMatches(
in: text, range: range, withTemplate: "#")
print(replaced)
// 选项:
// .caseInsensitive / .anchorsMatchLines

匹配

首次匹配、全部匹配、判断是否匹配。范围获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 判断是否匹配:
let email = "[email protected]"
// 完整匹配:
let full = /[a-z]+@[a-z]+\\.[a-z]+/
print(email.wholeMatch(of: full) != nil)
// 部分包含:
print(email.contains(/@/))
// 首次匹配:
let pattern = /\\d+/
if let m = "abc123def".firstMatch(of: pattern) {
print(m.0) // 123
}
// 全部匹配:
let all = "a1b2c3".matches(of: /\\d+/)
print(all.count) // 3
// 匹配位置:
// m.range 返回范围
// 前缀匹配:
if "2026-08".hasPrefix("2026") {}
// 注意:
// wholeMatch 完整匹配
// firstMatch 首次
// matches 全部
// 空匹配注意

捕获组

提取捕获组内容。命名捕获。正则分组引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 捕获组:
let pattern = /(\\d{4})-(\\d{2})-(\\d{2})/
let text = "2026-08-02"
if let m = text.wholeMatch(of: pattern) {
print(m.0) // 2026-08-02
print(m.1) // 2026
print(m.2) // 08
print(m.3) // 02
}
// 命名捕获:
let named = /(?<year>\\d{4})-(?<month>\\d{2})/
if let m = "2026-08".wholeMatch(of: named) {
print(m.year) // 2026
print(m.month) // 08
}
// 可选捕获:
// (x)? 匹配则可选
// 非捕获组:
// (?:x) 不捕获
// NSRegularExpression:
// m.range(at: 1) 提取组
// 用途:
// 解析结构化文本
// 提取 key=value

替换

正则替换文本。模板引用捕获组。条件替换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Foundation
// 字面替换:
let s = "Hello, world!"
let replaced = s.replacingOccurrences(of: "o", with: "0")
print(replaced) // Hell0, w0rld!
// 正则替换:
let text = "日期: 2026-08-02"
let regex = try! NSRegularExpression(pattern: "(\\d{4})-(\\d{2})-(\\d{2})")
let nsRange = NSRange(text.startIndex..., in: text)
// 模板引用组:
let formatted = regex.stringByReplacingMatches(
in: text, range: nsRange,
withTemplate: "$2/$3/$1")
print(formatted) // 日期: 08/02/2026
// 替换范围:
let trimmed = text.replacingOccurrences(
of: "\\s+", with: " ", options: .regularExpression)
// 模板特殊字符:
// $0 全匹配 $1 组1
// 条件替换需循环匹配

分割

按正则分割字符串。分隔符保留与删除。多分隔符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Foundation
// 字符串分割:
let csv = "a,b,c"
let parts = csv.split(separator: ",")
print(parts) // ["a", "b", "c"]
// 正则分割:
let text = "one two three"
let words = text.components(separatedBy: .whitespacesAndNewlines)
// 多分隔符:
let messy = "a;b,c|d"
let regex = try! NSRegularExpression(pattern: "[;,_|]+")
let splitParts = regex.split(messy)
// 保留空串:
// split 默认删空
// omittingEmptySubsequences
// 按行分割:
let lines = text.components(separatedBy: .newlines)
// 注意:
// 分隔符正则
// 拆分时去掉分隔符
// 大文本注意性能

常用模式

邮箱、URL、数字、手机号等常用正则模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// 邮箱:
let email = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/
// URL:
let url = /https?:\\/\\/[\\w.-]+(?:\\/[\\w._-]*)*/
// 整数:
let int = /^[+-]?\\d+$/
// 浮点数:
let float = /^[+-]?\\d+\\.\\d+$/
// 手机号:
let phone = /^1[3-9]\\d{9}$/
// 身份证:
let idCard = /^\\d{17}[0-9Xx]$/
// IP 地址:
let ip = /(?:\\d{1,3}\\.){3}\\d{1,3}/
// 中文:
let chinese = /[\\u4e00-\\u9fa5]+/
// 空格:
let spaces = /\\s+/
// 使用:
print("[email protected]".wholeMatch(of: email) != nil)
print("13800138000".wholeMatch(of: phone) != nil)
// 注意:
// 正则要经过测试
// 性能:复杂模式慎用
// 实际校验用专门的库

19.构建与工具

swiftc 编译、SwiftPM 构建、格式与检查工具。

swiftc 编译

命令行编译单文件。生成可执行。优化选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 编译执行文件:
# swiftc main.swift -o app
# 运行:
# ./app
# 直接解释:
# swift main.swift
# 多个源文件:
# swiftc main.swift utils.swift -o app
# 生成模块:
# swiftc -emit-module
# 优化:
# -O 优化编译
# -Ounchecked 更快但不检查
# -Onone 调试
# 调试信息:
# -g 生成调试符号
# 链接框架:
# -framework Foundation
# 交叉编译:
# -target x86_64-unknown-linux-gnu
# 查看版本:
# swift --version
# 说明:
# SwiftPM 管理多文件
# 单文件开发用 swiftc

SwiftPM 构建

swift build 编译项目。增量构建。发布模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 初始化项目:
# swift package init --type executable
# 构建:
# swift build
# 发布模式:
# swift build -c release
# 指定产品:
# swift build --product app
# 增量构建(默认)
# 清理:
# swift package clean
# 更新依赖:
# swift package resolve
# 查看依赖树:
# swift package show-dependencies
# 输出位置:
# .build/debug/ 和 .build/release/
# 运行可执行:
# .build/debug/myapp
# 说明:
# Package.swift 声明包
# 依赖用 git 地址/版本
# 构建产物在 .build/

运行测试

swift test 测试套件。XCTest 与测试目标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 运行测试:
# swift test
# 指定测试:
# swift test --filter TestMath
# 单个用例:
# swift test --filter TestMath/testAdd
# 并行:
# swift test --parallel
# 代码覆盖:
# swift test --enable-code-coverage
# 生成覆盖报告:
# llvm-cov report .build/debug/*.xctest
# 测试文件:
import XCTest
final class TestMath: XCTestCase {
func testAdd() {
XCTAssertEqual(add(1, 2), 3)
}
}
# 断言:
# XCTAssertTrue / XCTAssertNil
# 异步测试:
# XCTestExpectation
# 说明:
# 测试目标在 Package.swift 声明
# 命名 testXxx 自动发现

Package.swift

SwiftPM 包清单。目标与依赖声明。平台配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "MyApp",
// 平台:
platforms: [
.macOS(.v13)
],
// 产品:
products: [
.executable(name: "app", targets: ["App"]),
.library(name: "Core", targets: ["Core"]),
],
// 依赖:
dependencies: [
.package(url: "https://github.com/xxx/yyy", from: "1.0.0"),
],
// 目标:
targets: [
.executableTarget(
name: "App",
dependencies: ["Core", "yyy"]),
.target(name: "Core"),
.testTarget(
name: "AppTests",
dependencies: ["App"]),
]
)
# 依赖版本:
# from: 1.0.0 向上兼容
# exact: 1.2.3 精确
# branch: main

SwiftLint

代码风格检查。配置规则。CI 集成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 安装(Homebrew):
# brew install swiftlint
# 运行:
# swiftlint
# 修复:
# swiftlint autocorrect
# 指定目录:
# swiftlint lint --path Sources/
# 配置文件:
# 生成默认配置
# swiftlint generate-docs
# 常见规则:
# line_length 行长
# trailing_whitespace 尾随空格
# force_cast 强制转换
# force_unwrapping 强制解包
# 配置 .swiftlint.yml:
# disabled_rules:
# - force_cast
# opt_in_rules:
# - empty_count
# 忽略文件:
# excluded:
# - Generated/
# CI 集成:
# 失败则构建失败
# 保证风格一致

swift-format

官方代码格式化工具。格式规则与配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 格式化:
# swift-format format -i Sources/*.swift
# 检查:
# swift-format lint -r Sources/
# 配置:
# swift-format dump-configuration > .swift-format
# 与 Xcode 集成
# 配置示例:
# {
# "indentation": {
# "spaces": 4
# },
# "lineBreakBeforeEachArgument": false,
# "rules": {
# "AllPublicDeclarationsHaveDocumentation": false
# }
# }
# 使用方式:
# 编辑器保存时格式化
# pre-commit 钩子
# CI 检查格式
# 与 SwiftLint 区别:
# format 管格式
# lint 管风格规则
# 两者可配合

xcodebuild

Xcode 工程命令行构建。导出与归档。CI 使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 列出工程/工作区:
# xcodebuild -list
# 构建:
# xcodebuild -project App.xcodeproj \
# -scheme App -configuration Debug build
# 工作区构建:
# xcodebuild -workspace App.xcworkspace \
# -scheme App build
# 真机 SDK:
# -sdk iphoneos
# 模拟器:
# -destination 'platform=iOS Simulator,name=iPhone 15'
# 测试:
# xcodebuild test -scheme App
# 归档:
# xcodebuild archive -scheme App \
# -archivePath build/App.xcarchive
# 导出 IPA:
# xcodebuild -exportArchive \
# -exportOptionsPlist ExportOptions.plist
# CI 常用:
# 输出结果用 xcresulttool 分析

发布流程

版本控制、构建发布、分发。发布检查清单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 版本号规范:
# MAJOR.MINOR.PATCH
# 1.0.0 初始
# 2.1.0 新功能
# 2.1.3 修复
# Git 打标签:
# git tag v2.1.0
# git push --tags
# 发布流程:
# 1. 版本号 bump
# 2. CHANGELOG 更新
# 3. 测试全部通过
# 4. 构建发布版
# 5. 生成归档
# 6. 签名与公证
# 7. 上传分发
# 命令行工具分发:
# 静态编译
# tar 打包
# Homebrew formula
# 检查清单:
# 测试覆盖
# 文档更新
# 依赖锁定
# 产物验证
关于本速查

本页是 Swift 5.9 的自包含速查手册,覆盖语言核心与最常用标准库在真实项目中约 80% 的常见用法。内容偏向现代惯用法:值类型(struct/enum)、协议导向设计、async/await 结构化并发、if let 模式匹配、以及 Codable 序列化。权威参考见 Swift 官方语言指南与 API 设计规范。 19 个章节各自聚焦一个主题——从第一个程序到可选值、协议、并发与常见误区。每节拆成 8 个带示例的小节(每个 5–20 行),共约 150 个主题。代码片段刻意短小、自解释。 所有处理都在浏览器中完成——无上传、无追踪。本页是 GuruToolkit 免费开发者工具集的一部分;代码片段可自由使用,无任何担保。

版本 2.1.0