Swift: Swift 函数定义与参数:从入门到精通
函数是 Swift 中组织代码的基本单元。本课你将学会如何定义函数、灵活使用各种参数形式,让代码更语义化、更可复用。
1. 你将学到
- 使用
func关键字定义和调用函数 - 参数标签(argument label)与参数名的区别
- 为参数设置默认值
- 使用
inout实现参数的输入输出修改 - 使用可变参数(variadic parameters)处理不定数量参数
2. 一个后端工程师的真实故事
(1) 痛点:函数调用时参数含义不清晰
Bob 在开发一个电商订单系统,他写了一个计算订单总价的函数:
SWIFT
func calculate(price: Double, count: Int, rate: Double) -> Double {
return price * Double(count) * (1 - rate)
}
// 调用时——参数含义全靠猜
let result = calculate(price: 100, count: 3, rate: 0.1)
团队成员看到 rate: 0.1 时产生了分歧——有人认为是税率 10%,有人认为是折扣率 10%。函数签名没有表达出参数的语义角色。
(2) 参数标签的解法
Swift 允许为参数同时定义外部标签和内部名称,让调用点像自然语言一样可读:
SWIFT
func calculate(price: Double, count: Int, discount rate: Double) -> Double {
return price * Double(count) * (1 - rate)
}
// 调用时清晰可见:"discount" 表示折扣率
let result = calculate(price: 100, count: 3, discount: 0.1)
(3) 收益:语义清晰的函数调用
| 维度 | 改造前 | 改造后 |
|---|---|---|
| 参数含义 | 全靠猜测 | 调用点即可理解 |
| 代码可读性 | 需跳转到定义查看 | 单行即知语义 |
| 团队沟通成本 | 经常争论参数含义 | 函数签名自解释 |
| 维护效率 | 修改参数需全局搜索 | 局部修改无歧义 |
3. 函数的基本定义
函数是 Swift 中可复用的代码块,使用 func 关键字定义。
graph LR
A[func 关键字] --> B[函数名]
B --> C[参数列表]
C --> D[箭头返回值]
D --> E[函数体]
A --> F["func greet(name: String) -> String { return \"Hello \\(name)\" }"]
| 组成部分 | 是否必需 | 说明 |
|---|---|---|
func 关键字 |
✅ | 声明函数的标志 |
| 函数名 | ✅ | 标识函数,使用 camelCase |
参数列表 () |
✅ | 即使无参数也要写空括号 |
返回值 -> 类型 |
❌ | 省略表示返回 Void |
函数体 {} |
✅ | 函数执行的代码块 |
(1) 无参数无返回值
SWIFT
func sayHello() {
print("Hello!")
}
sayHello() // 调用
输出:
TEXT 📖 仅展示Hello!
(2) 参数与返回值
SWIFT
func add(a: Int, b: Int) -> Int {
return a + b
}
let sum = add(a: 5, b: 3)
print("5 + 3 = \(sum)")
输出:
TEXT 📖 仅展示5 + 3 = 8
▶ 示例:订单金额计算
SWIFT
// ============================================
// 计算含税订单金额
// ============================================
func orderAmount(price: Double, quantity: Int) -> Double {
return price * Double(quantity)
}
let amount = orderAmount(price: 29.99, quantity: 3)
print("订单金额:$\(amount)")
输出:
TEXT 📖 仅展示订单金额:$89.97
4. 参数标签与默认值
(1) 参数标签(Argument Label)
Swift 允许为每个参数指定外部标签(调用时使用)和内部名称(函数体内使用):
| 写法 | 语法 | 调用方式 | 适用场景 |
|---|---|---|---|
| 标签与名称相同 | func foo(name: String) |
foo(name: ...) |
参数名即有语义 |
| 外部标签 | func foo(with name: String) |
foo(with: ...) |
调用语句如自然语言 |
| 省略标签 | func foo(_ name: String) |
foo(...) |
动词已表达参数含义 |
| 前两个的组合 | 变量标签 | 灵活组合 | 自定义需求 |
▶ 示例:不同标签风格对比
SWIFT
// ============================================
// 参数标签的三种写法对比
// ============================================
// 1. 默认——标签=名称
func greet1(name: String) {
print("Hello, \(name)")
}
// 2. 自定义外部标签
func greet2(to name: String) {
print("Hello, \(name)")
}
// 3. 省略外部标签(下划线)
func greet3(_ name: String) {
print("Hello, \(name)")
}
greet1(name: "Alice")
greet2(to: "Bob")
greet3("Charlie")
输出:
TEXT 📖 仅展示Hello, Alice Hello, Bob Hello, Charlie
(2) 默认参数值
为参数指定默认值后,调用时可以省略该参数:
SWIFT
func makeCoffee(size: String, sugar: Int = 1) -> String {
return "\(size) coffee with \(sugar) sugar(s)"
}
print(makeCoffee(size: "Large")) // 使用默认糖量
print(makeCoffee(size: "Medium", sugar: 0)) // 自定义糖量
输出:
TEXT 📖 仅展示Large coffee with 1 sugar(s) Medium coffee with 0 sugar(s)💡 技巧: 默认参数应放在参数列表末尾,避免调用时产生歧义。
5. 可变参数与 inout
(1) 可变参数
可变参数接受零个或多个指定类型的值,在函数体内作为数组使用:
SWIFT
func sum(_ numbers: Double...) -> Double {
var total = 0.0
for number in numbers {
total += number
}
return total
}
print(sum(1, 2, 3)) // 3 个参数
print(sum(10, 20)) // 2 个参数
print(sum()) // 0 个参数
输出:
TEXT 📖 仅展示6.0 30.0 0.0
| 特点 | 说明 |
|---|---|
| 语法 | 在类型后加 ... |
| 数量 | 0 到任意多个 |
| 类型 | 所有值必须相同类型 |
| 位置 | 每个函数最多一个,且应放在最后 |
(2) inout 参数
inout 参数允许函数修改外部变量的值——本质是引用传递而非值拷贝:
SWIFT
// ============================================
// inout 参数:交换两个变量的值
// ============================================
func swapValues(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
var x = 10
var y = 20
swapValues(&x, &y)
print("x = \(x), y = \(y)")
输出:
TEXT 📖 仅展示x = 20, y = 10⚠️ 注意: 调用
inout参数时需在变量前加&符号。不能传递常量或字面量。
▶ 示例:万能平均值计算
SWIFT
// ============================================
// 可变参数 + inout 综合:计算平均分并记录次数
// ============================================
func average(_ scores: Double..., record callCount: inout Int) -> Double {
callCount += 1
guard !scores.isEmpty else { return 0 }
var total = 0.0
for score in scores {
total += score
}
return total / Double(scores.count)
}
var callTimes = 0
print("平均分:\(average(85, 90, 78, record: &callTimes))")
print("平均分:\(average(100, 95, record: &callTimes))")
print("函数已调用 \(callTimes) 次")
输出:
TEXT 📖 仅展示平均分:84.33333333333333 平均分:97.5 函数已调用 2 次
6. 完整示例:订单折扣计算器
SWIFT
// ============================================
// 完整示例:订单折扣计算器
// 功能:综合运用参数标签、默认值、可变参数、inout
// ============================================
import Foundation
// 1. 计算基础金额(可变参数)
func subtotal(_ prices: Double...) -> Double {
var total = 0.0
for price in prices {
total += price
}
return total
}
// 2. 计算折扣后的金额(默认参数 + 参数标签)
func applyDiscount(to total: Double, rate: Double = 0.1) -> Double {
return total * (1 - rate)
}
// 3. 记录订单日志(inout 参数)
func logOrder(_ description: String, counter: inout Int) {
counter += 1
print("[\(counter)] \(description)")
}
// 4. 模拟订单处理
var orderCount = 0
let item1 = subtotal(29.99, 49.99)
let item2 = subtotal(9.99, 14.99, 24.99)
logOrder("Item 1 subtotal: $\(item1)", counter: &orderCount)
logOrder("Item 2 subtotal: $\(item2)", counter: &orderCount)
let total1 = applyDiscount(to: item1, rate: 0.15)
let total2 = applyDiscount(to: item2)
let finalTotal = total1 + total2
logOrder("Final total: $\(finalTotal)", counter: &orderCount)
输出:
TEXT 📖 仅展示[1] Item 1 subtotal: $79.98 [2] Item 2 subtotal: $49.97 [3] Final total: $112.94
❓ 常见问题
Q 参数标签和参数名有什么区别?
A 参数标签(external name)用于调用时,参数名(internal name / local name)用于函数体内部。前者服务于调用点的可读性,后者服务于函数内部的语义。可以相同,也可以不同。
Q inout 参数和全局变量有什么区别?
A inout 参数在函数内部看起来像局部变量,函数结束时才写回原变量;全局变量在函数内可直接读写,但会降低代码的可维护性和可测试性。inout 是更安全的选择。
Q 可变参数和数组参数有什么区别?
A 可变参数在调用时不需要显式构造数组,直接传入逗号分隔的值即可;数组参数调用时必须传入
[1, 2, 3] 的形式。可变参数更简洁。Q 一个函数可以有多个可变参数吗?
A 不可以。每个函数最多只能有一个可变参数,且通常放在参数列表的末尾。如果确实需要多个不定数量参数,可以考虑使用数组参数。
Q 省略返回值箭头时函数返回什么?
A 省略
-> 类型 等同于返回 Void(即空元组 ())。函数体可以不写 return 语句。例如 func log() { print("log") } 返回 Void。📖 小节
- 函数用
func关键字定义,参数列表和函数体构成基本结构 - 参数标签让函数调用更接近自然语言,推荐为每个参数设计恰当的外部标签
- 默认参数值可以减少重复调用代码,应放在参数列表末尾
- 可变参数
...接收 0 到多个同类型值,在函数体内作为数组使用 inout参数实现引用传递,修改外部变量的值,调用时加&前缀
📝 作业
- 基础题: 编写一个
greet函数,接收name(String)和greeting(String,默认值 "Hello")两个参数,打印问候语。至少用两种不同的参数标签风格各调用一次。 - 进阶题: 编写一个
calculateBMI函数,接收weight(kg,Double)和height(m,Double),返回 BMI 值。使用参数标签让调用点读起来像calculateBMI(weight: 75, height: 1.8)。同时增加一个inout参数记录调用次数。 - 挑战题: 编写一个
gradesAnalyzer函数,使用可变参数接收任意数量的分数,返回最高分、最低分和平均分。然后用inout参数记录分析历史(每次分析的分数数量追加到一个数组中)。最后打印历史记录。