本节摘要:函数把一段输送逻辑封装成可复用的管道,闭包则是能随身携带的软管。本节讲参数标签与省略、默认值与可变参数、返回值与多元返回,再从"把函数当值传"过渡到闭包:表达式缩写、尾随闭包、捕获行为。第 3 章的按钮事件、第 5 章的每个 SwiftUI 花括号,全是本节的直接应用。
上一节的排程逻辑直接写在循环里,换个页面就要重抄一遍。函数解决的就是这个:起个名字、定好进水口(参数)与出水口(返回值),管子内部随便怎么拐。
// 基本形态:参数标签让调用处读起来像一句话 func daysUntilWater(plantName: String, intervalDays: Int, passedDays: Int) -> Int { let remaining = intervalDays - passedDays return remaining > 0 ? remaining : 0 } let d = daysUntilWater(plantName: "龟背竹", intervalDays: 3, passedDays: 1) print(d) // 2 // 省略外部标签:调用更像数学函数 func humidityRatio(_ current: Double, _ capacity: Double) -> Double { guard capacity > 0 else { return 0 } // 防除零,guard 详见 2.7 return current / capacity } print(humidityRatio(0.45, 1.2)) // 0.375
参数标签(外部名)与形参名(内部名)可以不同,这是 Swift 调用可读性的来源:daysUntilWater(plantName:intervalDays:passedDays:) 在调用处自解释,不需要跳进函数看参数含义。
默认值与可变参数让管子更灵活,多元返回值让一次输送带多样货:
// 默认值:不传就用默认,传了就覆盖 func waterReminder(for name: String, urgent: Bool = false) -> String { return urgent ? "\(name):立即浇水!" : "\(name):记得今天浇水" } print(waterReminder(for: "绿萝")) // 绿萝:记得今天浇水 print(waterReminder(for: "绿萝", urgent: true)) // 绿萝:立即浇水! // 可变参数:进水口数量不定,函数内拿到的是数组 func totalLeaves(counts: Int...) -> Int { return counts.reduce(0, +) } print(totalLeaves(counts: 12, 8, 25, 3)) // 48 // 元组返回:一次带回多件货,调用处按名取 func plantStatus(passed: Int, interval: Int) -> (message: String, urgent: Bool) { if passed >= interval + 2 { return ("严重缺水", true) } if passed >= interval { return ("该浇水了", false) } return ("状态良好", false) } let s = plantStatus(passed: 5, interval: 3) print(s.message, s.urgent) // 严重缺水 true
函数是钉在墙上的管道,闭包是能塞进口袋的一段逻辑——它是"值",可以存进变量、当参数传、被别人在未来的某个时刻调用。从最完整的写法到最简的尾随闭包,Swift 给了一条平滑的缩写通道:
let plants = ["龟背竹", "多肉", "虎皮兰", "绿萝"] // 完整闭包写法:{ (参数) -> 返回类型 in 语句 } let byLength = plants.sorted { (a: String, b: String) -> Bool in return a.count < b.count } print(byLength) // ["多肉", "绿萝", "龟背竹", "虎皮兰"] // 类型可推断后:去掉类型声明与 return let shortFirst = plants.sorted { $0.count < $1.count } // $0 $1 是匿名参数 // map:每个元素过一遍管子,出来一个新数组(第 4 章列表数据的常客) let reminders = plants.map { "🌵 \($0) 待检查" } print(reminders) // ["🌵 龟背竹 待检查", "🌵 多肉 待检查", ...] // filter:按条件筛出子集 let shortNames = plants.filter { $0.count <= 2 } print(shortNames) // ["多肉"]
in 这个词就是分水岭:左边是参数与返回类型声明,右边是管子本体。尾随闭包写在括号外,是 SwiftUI 语法观感的来源——Button { } 那个花括号,就是这里学的闭包。

背景:植物列表每行要一句倒计时文案,规则随品种微调,直接写在 cellForRow 里(第 4 章)会把界面代码泡进业务逻辑。操作:把规则收进函数,把格式收进闭包。
// 第一步:规则函数(纯逻辑,可在 Playground 验证) func countdownText(name: String, passed: Int, interval: Int) -> String { switch passed { case ..<0: return "\(name):数据异常" case let p where p >= interval: return "\(name):该浇水了" default: return "\(name):还剩 \(interval - passed) 天" } } // 第二步:准备数据并用 map 批量生成文案 let rows = [("龟背竹", 1, 3), ("绿萝", 2, 2), ("多肉", 4, 10)] let texts = rows.map { countdownText(name: $0.0, passed: $0.1, interval: $0.2) } texts.forEach { print($0) } // 输出: // 龟背竹:还剩 2 天 // 绿萝:该浇水了 // 多肉:还剩 6 天
结果:三行文案规则一致、可单测、可复用。解读:界面代码将来只负责把 texts 摆进单元格,文案怎么来的一概不问——这条"逻辑与视图分家"的线,到第 3 章 MVC 里会正式画出来。变式:规则升级为按品种带权重(多肉按 1.5 倍周期),只改 countdownText 一处;若规则要热更新,把函数换成存进变量的闭包,运行时就能替换。
_ 用于省略外部名;in 左声明右本体;[x] 拍快照。逻辑能封装了,还缺"装数据的容器"——下一节造细胞:枚举与结构体。