Swift 中的闭包(Closures):一窥现代编程语言的灵魂

前言

Swift 是一门现代化的编程语言,它在许多方面都充满创新和便利性。在 Swift 中,闭包 是一项强大的特性,它为开发者提供了一种灵活、轻量级的函数式编程方式。本文我将总结一下我深入研究后关于 Swift中 闭包 的概念,种类和写法以及一些实际的应用。

一、闭包的概念

相信大家网上一搜都查到这么一句话:

闭包是一种自包含的功能代码块,是可以被传递和用作参数的函数。

那么这句话应该如何理解呢?需要理解这句话,需要首先理解 Swift 中函数是一等公民(First-Class Citizen)的概念。

所谓"第一等公民"(first class),指的是其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

而Swift 中的函数和闭包都是一等公民,因此闭包既可以被当作参数传递给函数,也可以赋值给变量,还可以作为函数的返回值。

二、闭包的种类

闭包有三种形式:全局函数、嵌套函数和闭包表达式。

1.全局函数

全局函数是一个特殊的闭包。他具有名字但不捕获任何值,定义在全局作用域内,例如以下代码:

swift 复制代码
// 全局作用域中的函数
//`person是`greet` 函数的参数,而不是捕获的值。
func greet(person: String) -> String {
    return "Hello, \(person)!"
}

let greeting = greet(person: "Alice")
print(greeting) // 输出: Hello, Alice!

2.嵌套函数

嵌套函数是在其他函数内部定义的函数,也是一个特殊的闭包,它具有名字,可以捕获其包含函数的参数和常量。嵌套函数在外部是不可见的,除非被包含的函数调用了它。

swift 复制代码
func outerFunction() -> (String) -> String {
    func innerFunction(name: String) -> String {
        return "Welcome, \(name)!"
    }
    return innerFunction
}

let welcome = outerFunction()
let message = welcome("Bob")
print(message) // 输出: Welcome, Bob!

嵌套函数捕获的值会被保留在内存中,直到闭包不再被引用。从而保证了在下一次执行函数时,之前捕获的值依旧存在,也无需我们担心内存管理问题

swift 复制代码
func outerFunction() -> () -> Void {
    var value = 10
    
    func innerFunction() {
        value += 5
        print(value)
    }
    
    return innerFunction
}

let closure = outerFunction()
closure() // 输出 15
closure() // 输出 20

3.闭包表达式

3.1 闭包表达式的定义和格式

闭包表达式是一种更为简洁的方式定义闭包,通常在需要时进行直接定义和使用,也可以捕获上下文的变量/常量。

闭包表达式的基本语法包括参数列表、返回箭头和代码块。格式如下:

swift 复制代码
{ (parameters) -> returnType in  
    //statements  
}

举2个例子,分别是 无参数无返回值 和 有参数有返回值(自己可以推导出另外两种写法: 无参数有返回值 和 有参数无返回值)

swift 复制代码
//无参数无返回值-写法1
let log1: ()->Void = { ()->Void in
    print("closure")
}

//无参数无返回值-写法2
let log2 = { ()->Void in
            print("closure")
        } 

//调用方法
log1()
log2()
swift 复制代码
//有参数有返回值-写法1
let log1: (_ str:String)->Void = { (str:String)->Void in
    print(str)
    return str
}

//有参数有返回值-写法2
let log2: (_ str:String)->Void = { (str:String)->Void in
    print(str)
    return str
}

//调用方法
let str1 = log1("closure")
let str1 = log2("closure")
3.2 闭包表达式的优化

在 Swift 中,闭包表达式具有简洁、灵活的语法,并支持一些优化方式。以下是闭包表达式的优化方式:

  • 简化参数和返回类型

可以省略闭包参数和返回类型的声明,让 Swift 根据上下文进行类型推断。

swift 复制代码
// 完整形式
let sum = { (a: Int, b: Int) -> Int in
    return a + b
}

// 优化形式(优化了参数类型声明)
let sumOptimized = { a,b -> Int in
    return a + b
} 

用系统提供的排序函数sorted更直观,因为该函数是对比两个Int参数,返回谁更大谁更小的布尔值判断,即

swift 复制代码
{ (num1: Int, num2: Int) -> Bool in 
    return num1 < num2 
}

所以可以省略闭包参数和返回类型的声明,如下:

swift 复制代码
// 完整形式
let sortNumbers = numbers.sorted { (num1:Int, num2:Int) -> Bool in 
    return num1 < num2 
    }

// 优化形式(优化了参数类型和返回类型声明)
let sumSortNumbers = numbers.sorted { num1,num2 in 
    return num1 < num2 
}
  • 单表达式闭包

如果闭包只有一条语句,可以省略 return 关键字。

swift 复制代码
// 完整形式
let square = { (number: Int) -> Int in
    return number * number
}

// 优化形式
let squareOptimized = { (number: Int) -> Int in number * number } 
  • 参数名缩写

如果闭包体的参数和返回值可以被自动推断,那么可以使用 $0, $1, $2, ... 来代替闭包中的参数名,从而省略 in 关键字。(如果闭包体只有一行,并且该行内容可以被自动推断,不仅能省略 return 关键字,甚至还可以省略 in 关键字)

swift 复制代码
let numbers = [1, 2, 3, 4, 5]

// 完整形式
let mappedNumbers = numbers.map({ (number: Int) -> Int in
    return number * 2
})

// 优化形式(闭包体只有一行,可以省略 `return` 关键字)
let mappedNumbersOptimized = numbers.map { $0 * 2 }
  • 尾随闭包

在 Swift 中,如果函数的最后一个参数是闭包,而你又需要使用闭包作为函数的参数,那么就可以使用尾随闭包。

尾随闭包是一种在函数调用时编写的闭包表达式,它被写在函数调用的括号之后,尾随闭包的语法允许省略闭包的参数标签,让函数更加清晰易读。

swift 复制代码
func performOperation(_ name:String, _ operation: () -> Void) {
    // 执行操作
    operation()
}

// 完整形式
performOperation("perform",{
    print("Performing operation")
})

// 优化形式
performOperation("perform") {
    print("Performing operation")
} 

若函数只需要闭包表达式一个参数,当使用尾随闭包时,可以把()也省略掉。

swift 复制代码
func performOperation(_ operation: () -> Void) {
    // 执行操作
    operation()
}

// 完整形式
performOperation({
    print("Performing operation")
})

// 优化形式
performOperation{
    print("Performing operation")
} 

三、逃逸闭包

逃逸闭包(Escaping Closures)是指在闭包被传递到函数之外的作用域,或者在函数执行完之后才被调用的闭包。(在 Swift 中,闭包默认是非逃逸的,即在函数结束前执行。)

需要使用 @escaping 标记逃逸闭包,以便在函数执行完毕后继续存在。

逃逸闭包通常用于异步操作,例如在后台执行任务后执行闭包。

swift 复制代码
var completionHandlers: [() -> Void] = []

func doSomething(completion: @escaping () -> Void) {
    completionHandlers.append(completion)
}

func executeCompletionHandlers() {
    for handler in completionHandlers {
        handler()
    }
}

// 逃逸闭包
doSomething {
    print("Task completed!")
}

// 执行逃逸闭包
executeCompletionHandlers() // 输出: Task completed!

在这个例子中,doSomething 函数接受一个逃逸闭包作为参数,并将其添加到 completionHandlers 数组中。稍后,通过调用 executeCompletionHandlers 函数,之前添加的闭包会被执行。

四、闭包的应用场景

闭包在 Swift 中有许多应用场景,其中一些包括:

1. 排序操作: 使用闭包对数组进行自定义排序。

swift 复制代码
let numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
let sortedNumbers = numbers.sorted { $0 < $1 }
print(sortedNumbers) // 输出: [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]

2. 函数式编程: 利用闭包进行函数式编程,实现高阶函数的功能。

swift 复制代码
let numbers = [1, 2, 3, 4, 5]
let doubledNumbers = numbers.map { $0 * 2 }
print(doubledNumbers) // 输出: [2, 4, 6, 8, 10]

3. UI 动画: 使用闭包实现 UIView 动画的 completion 部分。

swift 复制代码
UIView.animate(withDuration: 1.0, animations: {
    // 动画效果
}, completion: { _ in
    // 动画完成后执行的闭包
    print("Animation completed!")
})

总结

Swift 中的闭包是一项强大而灵活的特性,它为开发者提供了一种简洁、优雅的函数式编程方式。深入理解和熟练使用闭包,将有助于提高 Swift 编程的效率和代码质量。

相关推荐
DisonTangor14 小时前
苹果发布iOS 18.2首个公测版:Siri接入ChatGPT、iPhone 16拍照按钮有用了
ios·chatgpt·iphone
- 羊羊不超越 -14 小时前
App渠道来源追踪方案全面分析(iOS/Android/鸿蒙)
android·ios·harmonyos
2401_865854881 天前
iOS应用想要下载到手机上只能苹果签名吗?
后端·ios·iphone
HackerTom2 天前
iOS用rime且导入自制输入方案
ios·iphone·rime
良技漫谈2 天前
Rust移动开发:Rust在iOS端集成使用介绍
后端·程序人生·ios·rust·objective-c·swift
2401_852403552 天前
高效管理iPhone存储:苹果手机怎么删除相似照片
ios·智能手机·iphone
星际码仔2 天前
【动画图解】是怎样的方法,能被称作是 Flutter Widget 系统的核心?
android·flutter·ios
emperinter2 天前
WordCloudStudio:AI生成模版为您的文字云创意赋能 !
图像处理·人工智能·macos·ios·信息可视化·iphone
关键帧Keyframe2 天前
音视频面试题集锦第 7 期
音视频开发·视频编码·客户端
关键帧Keyframe2 天前
音视频面试题集锦第 8 期
ios·音视频开发·客户端