1
2
3
4
5
| let squares = [1, 2, 3].map { (n: Int) -> Int in
return n * n
}
print(squares)
// prints: [1, 4, 9]
|
1
2
3
4
5
| let squares = [1, 2, 3].map { n in
n * n
}
print(squares)
// prints: [1, 4, 9]
|
单表达式闭包自动返回,不需要 return。
1
2
3
| let squares = [1, 2, 3].map { $0 * $0 }
print(squares)
// prints: [1, 4, 9]
|
1
2
3
4
5
6
7
8
9
10
| func retry(times: Int, task: () -> Void) {
for _ in 0..<times { task() }
}
retry(times: 2) { print("尝试一次") }
// prints: 尝试一次
// 尝试一次
// 等价于
retry(times: 2, task: { print("尝试一次") })
|
1
2
3
4
5
6
7
8
9
10
11
12
| func configure(setup: () -> Void, completion: () -> Void) {
setup()
completion()
}
configure {
print("setup")
} completion: {
print("completion")
}
// prints: setup
// completion
|
第一个闭包不带标签,后面的带。SwiftUI 里的 Button { } label: { }、UIKit 里的 UIView.animate(withDuration:) { } completion: { } 都靠它。
1
2
3
4
5
6
| print([1, 2, 3].reduce(0, +))
// prints: 6
print([3, 1, 2].sorted(by: >))
// prints: [3, 2, 1]
print([1, 2, 3].map(-))
// prints: [-1, -2, -3]
|
运算符本身就是函数,可以当参数传。
1
2
3
4
5
6
7
| struct Temperature {
var celsius: Double
var fahrenheit: Double {
get { celsius * 9 / 5 + 32 }
set { celsius = (newValue - 32) * 5 / 9 }
}
}
|
只读时可以把 get 和括号全部省掉:
1
2
3
4
| struct Temperature {
var celsius: Double
var fahrenheit: Double { celsius * 9 / 5 + 32 }
}
|
1
2
3
4
| var score = 0 {
willSet(newValue) { print("变成 \(newValue)") }
didSet(oldValue) { print("从 \(oldValue) 变成 \(score)") }
}
|
参数名有默认值,绝大多数时候直接省:
1
2
3
4
| var score = 0 {
willSet { print("变成 \(newValue)") }
didSet { print("从 \(oldValue) 变成 \(score)") }
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| struct Point { var x: Double; var y: Double }
// 一个 init 都不写,编译器白送一个"成员逐一初始化器"
print(Point(x: 1, y: 2))
// prints: Point(x: 1.0, y: 2.0)
// 想再加一个入口,就放进 extension —— 成员逐一初始化器依然在
extension Point {
init(square side: Double) {
self.init(x: side, y: side)
}
}
print(Point(square: 3))
// prints: Point(x: 3.0, y: 3.0)
|
⚠️ 但同签名的手写初始化器往哪塞都不行。extension Point { init(x: Double, y: Double) } 会直接撞上编译器合成的那份,报 invalid redeclaration of synthesized memberwise 'init(x:y:)'——它不会"顶掉"合成版本,只是纯粹报错。
💭 一句话记住:自定义初始化器写在类型声明体内,成员逐一初始化器就不再生成了;写在 extension 里,它就还在,前提是签名别撞车。
1
2
3
4
5
6
7
8
| struct Config { var name = "默认"; var retries = 3 }
let a = Config()
let b = Config(name: "自定义")
let c: Config = .init(retries: 5)
print(a.name, b.name, c.retries)
// prints: 默认 自定义 5
|
类型已经能从上下文推出来时,.init(...) 比 Config(...) 短一截。
1
2
3
4
| enum Direction { case north, south }
let a = Direction.north // 完整写法
let b: Direction = .north // 类型已知时省略类型名
|
⚠️ 这叫前导点(隐式成员表达式),前提是上下文类型已经确定。少了上下文就没法省:let b = .north 报的是 reference to member 'north' cannot be resolved without a contextual type;如果这个点号落在元组里(let t = (1, .north)),报的则是 cannot infer contextual base in reference to member 'north'——同一件事的两种说法。枚举 case、静态属性、静态方法、.init 全都适用,完整规律和几个坑见 06 可选值。
1
2
3
4
| let a: [String: Int] = ["x": 1]
let b = ["x": 1] // 推断为 [String: Int]
let c: [Any] = [1, "two"]
let d: [Int] = [] // 空字面量必须写类型
|
⚠️ 推断不是万能的:let e = [] 直接报错,因为编译器没有任何线索。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| let score = 87
// 老写法
let letter1: String
if score >= 90 { letter1 = "A" } else { letter1 = "B" }
// 5.9 起
let letter2 = if score >= 90 { "A" } else { "B" }
let grade = switch score {
case 90...: "A"
case 80..<90: "B"
default: "C"
}
print(letter1, letter2, grade)
// prints: B B B
|
1
2
3
4
5
| enum Weekday: Int { case mon = 1, tue, wed } // tue = 2, wed = 3
enum Suit: String { case hearts, spades } // "hearts", "spades"
print(Weekday.wed.rawValue, Suit.spades.rawValue)
// prints: 3 spades
|
整数原始值只写第一个,后面的自动递增;字符串原始值则默认等于 case 名。
1
2
3
4
5
6
7
8
9
10
11
12
13
| let value: Int? = 7
switch value {
case .some(let v): print(v) // 完整写法
case .none: break
}
switch value {
case let v?: print(v) // 后缀 ? 简写
case nil: break
}
if let v = value { print(v) }
|
三种写法等价。case let v? 在读别人的代码时会突然出现,认得就行。
1
2
3
4
5
6
7
8
| let raw: String? = "42"
if let text = raw, let value = Int(text) {
print(value)
} else {
print("没有数字")
}
// prints: 42
|
1
2
3
4
5
6
| let raw: String? = "42"
if let raw, let value = Int(raw) {
print(value)
}
// prints: 42
|
变量名相同时,右边可以直接省略。guard 同理:
1
2
3
4
5
6
| func handle(_ text: String?) {
guard let text else { return }
print(text)
}
handle("ok")
// prints: ok
|
1
2
3
| let raw: String? = nil
print(Int(raw ?? "") ?? 0)
// prints: 0
|
?? 的链式兜底能让"多层可选值"变成一行。
1
2
3
4
5
| let dict = ["key": "value"]
print(dict["missing"]?.count ?? -1)
// prints: -1
print(dict["key"]?.count ?? -1)
// prints: 5
|
1
2
3
4
5
6
7
| let raw: String? = "42"
let doubled: Int? = raw.map { Int($0) ?? 0 }.map { $0 * 2 }
print(doubled ?? -1)
// prints: 84
print([1, nil, 3].compactMap { $0 })
// prints: [1, 3]
|
可选值也有 map,写法比 if let 更紧凑,代价是可读性下降。
1
2
3
4
5
6
7
8
9
10
11
12
13
| struct Money: Equatable, Hashable {
var amount: Int
var currency: String
static func == (lhs: Money, rhs: Money) -> Bool {
lhs.amount == rhs.amount && lhs.currency == rhs.currency
}
func hash(into hasher: inout Hasher) {
hasher.combine(amount)
hasher.combine(currency)
}
}
|
1
2
3
4
5
6
7
| struct Money: Equatable, Hashable {
var amount: Int
var currency: String
}
print(Money(amount: 1, currency: "CNY") == Money(amount: 1, currency: "CNY"))
// prints: true
|
只要所有存储属性都满足条件,Equatable、Hashable、Codable 编译器都会替你补上。
⚠️ 但 CaseIterable 不在这个名单里:它是"枚举专用"的,只有没有关联值的枚举才能合成 allCases。结构体哪怕全是简单属性也不行:
1
2
3
4
5
6
| enum Suit: CaseIterable { case hearts, spades }
print(Suit.allCases.count) // prints: 2 ✅
struct Bad: CaseIterable { var a: Int }
// 🛑 error: type 'Bad' does not conform to protocol 'CaseIterable'
// note: protocol requires property 'allCases'
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| protocol Greeter { var name: String { get } }
extension Greeter {
func greet() -> String { "你好,\(name)" }
}
struct Guest: Greeter { let name: String }
struct Robot: Greeter {
let name: String
func greet() -> String { "\(name) 上线" }
}
print(Guest(name: "小明").greet(), Robot(name: "R2").greet())
// prints: 你好,小明 R2 上线
|
⚠️ 想让默认实现在 any 上也生效,必须把方法同时写进协议要求里,否则会被静态派发。细节见 08 协议与泛型。
1
2
3
4
5
| struct Wrapper<T> { var value: T }
extension Wrapper: Equatable where T: Equatable {}
print(Wrapper(value: "a") == Wrapper(value: "a"))
// prints: true
|
一行声明让泛型类型"在条件满足时才具备某项能力",标准库的 Array、Optional 全靠这个机制。