1. Swift函数基础概述
在Swift编程语言中,函数是执行特定任务的自包含代码块。作为现代编程语言的核心构建块,Swift函数不仅继承了传统函数的基本特性,还引入了许多创新功能。我最初接触Swift函数时,就被其简洁而强大的表达能力所吸引 - 从简单的数学计算到复杂的业务逻辑,函数都能优雅地实现。
Swift 5.9版本对函数系统进行了多项优化,包括更高效的参数传递机制和改进的闭包性能。这些改进使得Swift函数在保持易用性的同时,执行效率接近C语言级别。对于iOS/macOS开发者而言,掌握Swift函数是构建任何应用程序的基础。
提示:Swift函数支持多种高级特性,如嵌套函数、函数类型和闭包表达式,这些特性让代码组织更加灵活。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 函数定义与基本语法
2.1 函数声明格式
Swift函数的标准定义格式如下:
swift复制func 函数名(参数名1: 参数类型, 参数名2: 参数类型) -> 返回类型 {
// 函数体
return 返回值
}
这里有几个关键点需要注意:
func是声明函数的关键字- 参数列表需要明确指定参数名和类型
- 返回类型使用
->符号指示 - 如果函数没有返回值,可以省略
-> 返回类型部分
一个实际的加法函数示例:
swift复制func addNumbers(a: Int, b: Int) -> Int {
let sum = a + b
return sum
}
2.2 参数标签与参数名
Swift函数的参数系统设计非常精细,它区分了参数标签(argument label)和参数名(parameter name):
swift复制func greet(person name: String) -> String {
return "Hello, \(name)!"
}
在这个例子中:
person是外部调用时使用的参数标签name是函数内部使用的参数名- 调用方式:
greet(person: "John")
如果不需要外部参数标签,可以使用下划线_忽略:
swift复制func power(_ base: Int, _ exponent: Int) -> Int {
return Int(pow(Double(base), Double(exponent)))
}
这样调用时更简洁:power(2, 3)
2.3 返回值处理
Swift函数可以返回任何类型的值,包括元组:
swift复制func minMax(array: [Int]) -> (min: Int, max: Int)? {
guard !array.isEmpty else { return nil }
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
这个函数返回一个包含最小值和最大值的元组,同时考虑了空数组的情况,返回可选类型。
3. 函数参数高级特性
3.1 默认参数值
Swift允许为参数提供默认值,这大大提高了API的灵活性:
swift复制func joinStrings(_ strings: [String], separator: String = ", ") -> String {
return strings.joined(separator: separator)
}
调用时可以选择是否提供分隔符:
swift复制joinStrings(["A", "B", "C"]) // 使用默认分隔符", "
joinStrings(["A", "B", "C"], separator: " | ") // 自定义分隔符
3.2 可变参数
使用...语法可以定义接受任意数量参数的函数:
swift复制func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
调用示例:
swift复制arithmeticMean(1, 2, 3, 4, 5)
arithmeticMean(3, 8.25, 18.75)
注意:一个函数最多只能有一个可变参数,且通常放在参数列表的最后。
3.3 输入输出参数
使用inout关键字可以创建能修改外部变量的参数:
swift复制func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
调用时需要加上&前缀:
swift复制var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
4. 函数类型与高阶函数
4.1 函数作为类型
在Swift中,函数也是一种类型,由参数类型和返回类型组成:
swift复制func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
// 函数类型为 (Int, Int) -> Int
var mathFunction: (Int, Int) -> Int = addTwoInts
这样可以将函数赋值给变量,或者作为参数传递。
4.2 函数作为参数
函数可以作为参数传递给其他函数:
swift复制func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5) // 输出: Result: 8
4.3 函数作为返回值
函数也可以返回另一个函数:
swift复制func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward
}
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
5. 闭包表达式
5.1 闭包基本语法
闭包是自包含的函数代码块,Swift中的闭包有几种简化形式:
swift复制// 完整形式
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
var reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
// 类型推断简化
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
// 单表达式隐式返回
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
// 参数名称缩写
reversedNames = names.sorted(by: { $0 > $1 } )
// 运算符方法
reversedNames = names.sorted(by: >)
5.2 尾随闭包
当闭包是函数的最后一个参数时,可以使用尾随闭包语法:
swift复制func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函数体
}
// 不使用尾随闭包
someFunctionThatTakesAClosure(closure: {
// 闭包体
})
// 使用尾随闭包
someFunctionThatTakesAClosure() {
// 闭包体
}
对于sorted方法可以这样写:
swift复制reversedNames = names.sorted { $0 > $1 }
5.3 值捕获
闭包可以捕获和存储其所在上下文中任何常量和变量的引用:
swift复制func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen() // 返回10
incrementByTen() // 返回20
6. 逃逸闭包与非逃逸闭包
6.1 逃逸闭包
当闭包作为参数传递给函数,但在函数返回后才被调用时,需要使用@escaping标记:
swift复制var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
6.2 非逃逸闭包
默认情况下,闭包是非逃逸的,意味着它必须在函数结束前被调用:
swift复制func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
7. 自动闭包
自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式:
swift复制var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
8. 函数式编程实践
8.1 map函数应用
swift复制let numbers = [1, 2, 3, 4, 5]
let squaredNumbers = numbers.map { $0 * $0 }
print(squaredNumbers) // [1, 4, 9, 16, 25]
8.2 filter函数应用
swift复制let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers) // [2, 4]
8.3 reduce函数应用
swift复制let sum = numbers.reduce(0, +)
print(sum) // 15
9. 函数性能优化技巧
9.1 使用@inlinable
对于小型、频繁调用的函数,可以使用@inlinable提示编译器进行内联优化:
swift复制@inlinable
public func square(_ x: Int) -> Int {
return x * x
}
9.2 避免过度使用闭包捕获
闭包捕获上下文变量会带来额外开销,对于性能关键代码应尽量减少捕获:
swift复制// 不推荐
func createMultiplier(factor: Int) -> (Int) -> Int {
return { number in
return number * factor
}
}
// 推荐(如果可能)
func multiply(_ number: Int, by factor: Int) -> Int {
return number * factor
}
9.3 使用@_specialize优化泛型函数
对于特定类型的泛型函数,可以使用@_specialize属性提示编译器生成优化版本:
swift复制@_specialize(where T == Int)
@_specialize(where T == Double)
func process<T: Numeric>(_ value: T) -> T {
return value * value
}
10. 函数错误处理
10.1 抛出函数
使用throws关键字标记可能抛出错误的函数:
swift复制enum FileError: Error {
case fileNotFound
case permissionDenied
}
func readFile(at path: String) throws -> String {
guard FileManager.default.fileExists(atPath: path) else {
throw FileError.fileNotFound
}
return try String(contentsOfFile: path)
}
10.2 错误捕获
使用do-catch语句捕获和处理错误:
swift复制do {
let content = try readFile(at: "/path/to/file")
print(content)
} catch FileError.fileNotFound {
print("文件未找到")
} catch FileError.permissionDenied {
print("权限不足")
} catch {
print("其他错误: \(error)")
}
10.3 rethrows函数
函数参数中的闭包抛出错误时,可以使用rethrows将错误传递出去:
swift复制func processNumbers(_ numbers: [Int], using processor: (Int) throws -> Int) rethrows -> [Int] {
var result = [Int]()
for number in numbers {
try result.append(processor(number))
}
return result
}
11. 函数并发与异步
11.1 async/await基础
Swift 5.5引入了结构化并发,使用async和await关键字:
swift复制func fetchUserID() async -> Int {
// 模拟网络请求
try? await Task.sleep(nanoseconds: 1_000_000_000)
return Int.random(in: 1...1000)
}
func fetchUsername(id: Int) async -> String {
// 模拟网络请求
try? await Task.sleep(nanoseconds: 1_000_000_000)
return "User_\(id)"
}
func getUser() async {
let id = await fetchUserID()
let name = await fetchUsername(id: id)
print("User: \(name)")
}
11.2 并行执行
使用async let实现并行执行:
swift复制func getHighScores() async {
async let score1 = fetchScore(for: "player1")
async let score2 = fetchScore(for: "player2")
async let score3 = fetchScore(for: "player3")
let scores = await [score1, score2, score3]
print("最高分: \(scores.max() ?? 0)")
}
11.3 任务组
对于动态数量的并行任务,可以使用任务组:
swift复制func fetchAllUsers(ids: [Int]) async throws -> [String] {
try await withThrowingTaskGroup(of: String.self) { group in
for id in ids {
group.addTask {
try await fetchUsername(id: id)
}
}
var names = [String]()
for try await name in group {
names.append(name)
}
return names
}
}
12. 函数内存管理
12.1 捕获列表
在闭包中使用捕获列表可以控制捕获方式:
swift复制class SomeClass {
var value = 0
func doSomething() {
someFunctionWithEscapingClosure { [weak self] in
guard let self = self else { return }
print(self.value)
}
}
}
12.2 自动引用计数
理解函数和闭包如何影响引用计数:
swift复制class DataImporter {
var filename = "data.txt"
// 数据导入类
}
class DataManager {
lazy var importer = DataImporter()
var data = [String]()
lazy var someClosure: () -> Void = { [unowned self] in
print(self.data.count)
}
}
13. 函数调试技巧
13.1 打印函数调用
使用#function获取当前函数名:
swift复制func logEnteringFunction() {
print("Entering \(#function)")
}
13.2 条件断点
在Xcode中为函数设置条件断点:
- 在函数行号处点击添加断点
- 右键断点选择"Edit Breakpoint"
- 设置条件或添加调试动作
13.3 测量执行时间
使用ContinuousClock测量函数执行时间:
swift复制func measureTime() {
let clock = ContinuousClock()
let result = clock.measure {
// 要测量的代码
computeSomethingIntensive()
}
print("执行时间: \(result)")
}
14. 函数测试策略
14.1 单元测试基础
为函数编写单元测试:
swift复制import XCTest
class MathTests: XCTestCase {
func testAddition() {
XCTAssertEqual(addNumbers(a: 2, b: 3), 5)
XCTAssertEqual(addNumbers(a: -1, b: 1), 0)
XCTAssertEqual(addNumbers(a: 0, b: 0), 0)
}
}
14.2 异步测试
测试异步函数:
swift复制func testAsyncFunction() async throws {
let result = await fetchSomeData()
XCTAssertNotNil(result)
}
14.3 性能测试
测量函数性能:
swift复制func testPerformance() {
measure {
computeSomethingIntensive()
}
}
15. 函数文档规范
15.1 文档注释格式
使用Markdown格式的文档注释:
swift复制/// 计算两个数的和
///
/// - Parameters:
/// - a: 第一个加数
/// - b: 第二个加数
/// - Returns: 两个参数的和
///
/// - Note: 这个函数不处理溢出情况
///
/// - Example:
/// ```
/// let sum = addNumbers(a: 2, b: 3) // 返回5
/// ```
func addNumbers(a: Int, b: Int) -> Int {
return a + b
}
15.2 生成文档工具
使用swift-doc生成HTML文档:
- 安装:
brew install swift-doc - 生成:
swift doc generate Sources --module-name MyModule --output docs
16. 函数最佳实践
16.1 单一职责原则
每个函数应该只做一件事:
swift复制// 不好
func processDataAndSave() {
// 处理数据
// 保存数据
}
// 好
func processData() -> ProcessedData {
// 处理数据
}
func saveData(_ data: ProcessedData) {
// 保存数据
}
16.2 合理控制函数长度
理想情况下,函数应该能在一屏内显示(约20-30行)。如果函数过长,考虑拆分为多个小函数。
16.3 有意义的命名
函数名应该清晰表达其意图:
- 不好:
func handle() - 好:
func calculateAverageTemperature()
16.4 避免副作用
理想情况下,函数应该只通过返回值与外界通信,避免修改外部状态:
swift复制// 不好
var globalCounter = 0
func incrementCounter() {
globalCounter += 1
}
// 好
func incrementCounter(_ counter: Int) -> Int {
return counter + 1
}
17. 函数版本兼容性
17.1 @available属性
标记函数的平台和版本要求:
swift复制@available(iOS 15, macOS 12, *)
func useNewFeature() {
// 使用iOS 15/macOS 12的新API
}
17.2 #available条件
在函数内检查API可用性:
swift复制func doSomething() {
if #available(iOS 15, *) {
// 使用新API
} else {
// 回退方案
}
}
17.3 @_disfavoredOverload
当引入新版本函数时,可以标记旧版本为不推荐:
swift复制@_disfavoredOverload
func process(data: Data) -> Result {
// 旧实现
}
func process(data: Data) async throws -> Result {
// 新实现
}
18. 函数与协议
18.1 协议中的函数要求
协议可以定义函数要求:
swift复制protocol DataProcessor {
func process(data: Data) -> ProcessedData
mutating func updateConfiguration(_ config: Configuration)
static func createDefault() -> Self
}
18.2 协议扩展默认实现
为协议函数提供默认实现:
swift复制extension DataProcessor {
func process(data: Data) -> ProcessedData {
// 默认实现
}
}
18.3 函数式协议
创建函数式风格的协议:
swift复制protocol Mapper {
associatedtype Input
associatedtype Output
func map(_ input: Input) -> Output
}
struct StringToIntMapper: Mapper {
func map(_ input: String) -> Int {
return Int(input) ?? 0
}
}
19. 函数与泛型
19.1 泛型函数基础
swift复制func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
19.2 类型约束
为泛型参数添加约束:
swift复制func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
19.3 where子句
使用where添加更复杂的约束:
swift复制func allItemsMatch<C1: Collection, C2: Collection>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.Element == C2.Element, C1.Element: Equatable {
// 检查两个容器是否包含相同顺序的相同元素
return someContainer.count == anotherContainer.count &&
zip(someContainer, anotherContainer).allSatisfy { $0 == $1 }
}
20. 函数与SwiftUI
20.1 作为视图构建器
函数可以返回SwiftUI视图:
swift复制func makeHeader(title: String) -> some View {
Text(title)
.font(.largeTitle)
.padding()
}
20.2 与@ViewBuilder结合
使用@ViewBuilder创建复杂视图:
swift复制@ViewBuilder
func makeSection(isExpanded: Bool) -> some View {
if isExpanded {
Text("详细内容")
Image(systemName: "chevron.up")
} else {
Text("点击展开")
Image(systemName: "chevron.down")
}
}
20.3 与Binding配合
创建处理绑定的函数:
swift复制func textFieldWithValidator(text: Binding<String>, validator: @escaping (String) -> Bool) -> some View {
VStack {
TextField("输入", text: text)
.border(validator(text.wrappedValue) ? Color.green : Color.red)
}
}
