Swift: Swift 函数定义与参数:从入门到精通

函数是 Swift 中组织代码的基本单元。本课你将学会如何定义函数、灵活使用各种参数形式,让代码更语义化、更可复用。

1. 你将学到


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 关键字定义。

100%
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

📖 小节


📝 作业

  1. 基础题: 编写一个 greet 函数,接收 name(String)和 greeting(String,默认值 "Hello")两个参数,打印问候语。至少用两种不同的参数标签风格各调用一次。
  2. 进阶题: 编写一个 calculateBMI 函数,接收 weight(kg,Double)和 height(m,Double),返回 BMI 值。使用参数标签让调用点读起来像 calculateBMI(weight: 75, height: 1.8)。同时增加一个 inout 参数记录调用次数。
  3. 挑战题: 编写一个 gradesAnalyzer 函数,使用可变参数接收任意数量的分数,返回最高分、最低分和平均分。然后用 inout 参数记录分析历史(每次分析的分数数量追加到一个数组中)。最后打印历史记录。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏