本篇目标

掌握 Swift 函数的定义、参数、返回值,理解闭包的语法与用法,学会使用常用的高阶函数。

一、函数基础

定义函数:

func greet() {
    print("Hello, Swift!")
}

greet()  // 调用函数

带参数:

func greet(name: String) {
    print("Hello, \(name)!")
}

greet(name: "Tom")

带返回值:

func add(a: Int, b: Int) -> Int {
    return a + b
}

let result = add(a: 3, b: 5)  // 8

多返回值(使用元组):

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 {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

if let result = minMax(array: [3, 1, 5, 2, 4]) {
    print(result.min)  // 1
    print(result.max)  // 5
}

二、参数标签

Swift 函数参数有外部标签和内部名称。

func greet(person name: String) {
    print("Hello, \(name)")
}

greet(person: "Tom")
  • person 是外部标签,调用时使用。

  • name 是内部名称,函数体内使用。

省略外部标签:

func add(_ a: Int, _ b: Int) -> Int {
    return a + b
}

add(3, 5)  // 不需要写标签

使用 `_` 省略:

func greet(_ name: String) {
    print("Hello, \(name)")
}

greet("Tom")

三、默认参数值

func greet(name: String = "Guest") {
    print("Hello, \(name)")
}

greet()              // Hello, Guest
greet(name: "Tom")   // Hello, Tom

四、可变参数

func sum(_ numbers: Int...) -> Int {
    var total = 0
    for number in numbers {
        total += number
    }
    return total
}

sum(1, 2, 3)       // 6
sum(1, 2, 3, 4)    // 10
  • 可变参数在函数内部当作数组处理。

  • 一个函数只能有一个可变参数。

五、输入输出参数

使用 inout 在函数内部修改外部变量。

func swapValues(_ a: inout Int, _ b: inout Int) {
    let temp = a
    a = b
    b = temp
}

var x = 1
var y = 2
swapValues(&x, &y)
print(x, y)  // 2, 1

注意:

  • 调用时参数前加 &。

  • inout 参数不能有默认值。

  • 不能传入常量或字面量。

六、函数类型

函数本身也是一种类型,可以赋值给变量。

func add(a: Int, b: Int) -> Int {
    return a + b
}

var mathFunction: (Int, Int) -> Int = add
let result = mathFunction(3, 5)  // 8

函数作为参数:

func calculate(a: Int, b: Int, operation: (Int, Int) -> Int) -> Int {
    return operation(a, b)
}

let sum = calculate(a: 3, b: 5, operation: add)  // 8

函数作为返回值:

func chooseFunction(isAdd: Bool) -> (Int, Int) -> Int {
    return isAdd ? add : { $0 - $1 }
}

七、嵌套函数

函数内部可以定义函数。

func outer(value: Int) -> Int {
    func inner(x: Int) -> Int {
        return x * 2
    }
    return inner(x: value) + 1
}

print(outer(value: 5))  // 11

用途:

  • 封装只在内部使用的逻辑。

  • 配合闭包使用。

八、闭包基础

闭包是自包含的代码块,可以在代码中传递和使用。

闭包语法:

{ (参数) -> 返回类型 in
    代码
}

示例:

let greet = { (name: String) -> String in
    return "Hello, \(name)"
}

print(greet("Tom"))  // Hello, Tom

省略类型(类型推断):

let greet = { name in
    return "Hello, \(name)"
}

省略 return(单表达式):

let greet = { (name: String) -> String in
    "Hello, \(name)"
}

参数简写:

let add = { $0 + $1 }
print(add(3, 5))  // 8

九、尾随闭包

当闭包是函数的最后一个参数时,可以写成尾随闭包。

func performAction(action: () -> Void) {
    action()
}

// 普通写法
performAction(action: {
    print("执行操作")
})

// 尾随闭包
performAction {
    print("执行操作")
}

多个闭包参数:

func load(success: () -> Void, failure: () -> Void) {
    // ...
}

load {
    print("成功")
} failure: {
    print("失败")
}

十、逃逸闭包与非逃逸闭包

非逃逸闭包(默认):闭包在函数返回前执行完毕。

func perform(action: () -> Void) {
    action()
}

逃逸闭包:闭包可能在函数返回后执行,需要加 @escaping。

var completionHandlers: [() -> Void] = []

func addCompletion(handler: @escaping () -> Void) {
    completionHandlers.append(handler)
}

常见逃逸场景:

  • 异步回调

  • 存储闭包属性

  • 网络请求完成回调

func fetchData(completion: @escaping (String) -> Void) {
    DispatchQueue.global().async {
        // 模拟网络请求
        let data = "服务器返回的数据"
        DispatchQueue.main.async {
            completion(data)
        }
    }
}

十一、自动闭包

@autoclosure 把表达式自动包装成闭包。

func log(_ message: @autoclosure () -> String) {
    print(message())
}

log("这是一条日志")  // 不需要写 { }

注意:

  • 自动闭包让调用更简洁。

  • 不要滥用,会影响可读性。

十二、常用高阶函数

map:对每个元素做转换。

let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled)  // [2, 4, 6, 8, 10]

filter:筛选符合条件的元素。

let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers)  // [2, 4]

reduce:把数组归约为一个值。

let sum = numbers.reduce(0) { $0 + $1 }
print(sum)  // 15

let sum2 = numbers.reduce(0, +)
print(sum2)  // 15

sorted:排序。

let sorted = numbers.sorted { $0 > $1 }
print(sorted)  // [5, 4, 3, 2, 1]

compactMap:过滤 nil 并转换。

let strings = ["1", "2", "abc", "4"]
let numbers2 = strings.compactMap { Int($0) }
print(numbers2)  // [1, 2, 4]

flatMap:展平嵌套数组。

let nested = [[1, 2], [3, 4], [5]]
let flat = nested.flatMap { $0 }
print(flat)  // [1, 2, 3, 4, 5]

十三、避坑指南

  • 闭包循环引用:闭包捕获 self 时注意使用 [weak self]。

  • @escaping 不要漏写:异步回调必须加。

  • 尾随闭包可读性:闭包较长时,适当换行。

  • $0、$1 简写:参数多时不建议用,影响可读性。

  • map 与 forEach 区别:map 有返回值,forEach 没有。

  • reduce 初始值:不要忘记传初始值。

十四、检查清单

  • [ ] 能定义带参数和返回值的函数

  • [ ] 理解参数标签与内部名称

  • [ ] 会使用默认参数、可变参数、inout 参数

  • [ ] 理解函数类型

  • [ ] 会写闭包,掌握简写形式

  • [ ] 理解尾随闭包、逃逸闭包

  • [ ] 会使用 map、filter、reduce