Swift 闭包的使用与优势
1. 单语句闭包的简化
在 Swift 中,如果闭包体仅由单个语句组成,我们可以省略
return
关键字,该语句的结果将自动返回。例如:
let clos7 = {
(first: Int, second: Int) -> Int in
first + second
}
在这个例子里,闭包接受两个
Int
类型的参数,并返回一个
Int
类型的值。闭包体中的唯一语句是将第一个参数与第二个参数相加。这里没有在加法语句前使用
return
关键字,Swift 会识别这是一个单语句闭包,并自动返回结果,就好像我们添加了
return
关键字一样。需要注意的是,语句的结果类型必须与闭包的返回类型相匹配。
2. 结合数组算法使用闭包
我们以
map
算法为例,先定义一个数组:
let guests = ["Jon", "Kim", "Kailey", "Kara"]
以下是几种使用闭包与
map
算法的示例:
-
打印问候语
:
guests.map({
(name: String) -> Void in
print("Hello \(name)")
})
使用简写语法可简化为:
guests.map({print("Hello \($0)")})
- 返回包含问候语的新数组 :
var messages = guests.map({
(name:String) -> String in
return "Welcome \(name)"
})
当我们需要多次使用闭包或者在不同数组上使用相同闭包时,可以将闭包赋值给常量或变量,例如:
let greetGuest = {
(name:String) -> Void in
print("Hello guest named \(name)")
}
let sayGoodbye = {
(name:String) -> Void in
print("Goodbye \(name)")
}
guests.map(greetGuest)
guests.map(sayGoodbye)
我们还可以在闭包中对数组进行过滤操作,例如:
let greetGuest2 = {
(name:String) -> Void in
if (name.hasPrefix("K")) {
print("\(name) is on the guest list")
} else {
print("\(name) was not invited")
}
}
闭包能够捕获和存储其定义上下文内的任何变量或常量的引用。例如下面的温度计算示例:
func temperatures(calculate:(Int)->Void) {
var tempArray = [72,74,76,68,70,72,66]
tempArray.map(calculate)
}
func testFunction() {
var total = 0
var count = 0
let addTemps = {
(num: Int) -> Void in
total += num
count++
}
temperatures(addTemps)
print("Total: \(total)")
print("Count: \(count)")
print("Average: \(total/count)")
}
在
testFunction
中,
addTemps
闭包能够更新和使用在其定义上下文中的
total
和
count
变量,即使该闭包在
temperatures
函数中被调用。
3. 独立闭包与良好的代码风格
闭包使我们能够将代码的数据部分与用户界面和业务逻辑部分真正分离,有助于创建专注于数据检索的可重用类。以下是一个示例:
class Guests {
var guestNames = ["Jon","Kim","Kailey","Kara","Buddy","Lily","Dash"]
typealias UseArrayClosure = [String] -> Void
func getGuest(handler:UseArrayClosure) {
handler(guestNames)
}
}
在这个
Guests
类中,我们定义了一个
guestNames
数组,并创建了一个类型别名
UseArrayClosure
。
getGuest
方法接受一个符合
UseArrayClosure
类型的闭包作为参数,并执行该闭包。
假设我们要在
UITableView
中显示这些名字,在视图控制器中可以这样做:
@IBOutlet var tableView:UITableView?
var tableData: [String]?
func getData() {
let dataClosure: Guests.UseArrayClosure = {
self.tableData = $0
if let tView = self.tableView {
tView.reloadData()
}
}
let guests = Guests()
guests.getGuest(dataClosure)
}
通过使用闭包,我们可以异步调用网络服务,当数据返回时,闭包会自动执行,避免了 UI 冻结的问题。
4. 改变功能
闭包还能让我们动态改变类的功能。例如,定义一个
TestClass
:
class TestClass {
typealias getNumClosure = ((Int, Int) -> Int)
var numOne = 5
var numTwo = 8
var results = 0
func getNum(handler: getNumClosure) -> Int {
results = handler(numOne,numTwo)
return results
}
}
然后定义几个符合
getNumClosure
类型的闭包:
var max: TestClass.getNumClosure = {
if $0 > $1 {
return $0
} else {
return $1
}
}
var min: TestClass.getNumClosure = {
if $0 < $1 {
return $0
} else {
return $1
}
}
var multiply: TestClass.getNumClosure = {
return $0 * $1
}
var second: TestClass.getNumClosure = {
return $1
}
var answer: TestClass.getNumClosure = {
var tmp = $0 + $1
return 42
}
我们可以将这些闭包传递给
TestClass
的
getNum
方法,以改变函数的功能:
var myClass = TestClass()
myClass.getNum(max)
myClass.getNum(min)
myClass.getNum(multiply)
myClass.getNum(second)
myClass.getNum(answer)
不同闭包的执行结果如下表所示:
| 闭包名称 | 结果 |
| ---- | ---- |
| max | 8 |
| min | 5 |
| multiply | 40 |
| second | 8 |
| answer | 42 |
5. 根据结果选择闭包
在某些情况下,我们会根据一定的逻辑选择执行不同的闭包。例如,定义一个
TestClass
:
class TestClass {
typealias ResultsClosure = ((String) -> Void)
func isGreater(numOne: Int, numTwo:Int, successHandler:
ResultsClosure, failureHandler: ResultsClosure) {
if numOne > numTwo {
successHandler("\(numOne) is greater than \(numTwo)")
}
else {
failureHandler("\(numOne) is not greater than \(numTwo)")
}
}
}
再定义两个闭包:
var success: TestClass. ResultsClosure = {
print("Success: \($0)")
}
var failure: TestClass. ResultsClosure = {
print("Failure: \($0)")
}
在
isGreater
方法中,根据
numOne
和
numTwo
的大小关系,选择执行
successHandler
或
failureHandler
闭包。整个流程可以用以下 mermaid 流程图表示:
graph TD;
A[开始] --> B{numOne > numTwo?};
B -- 是 --> C[执行 successHandler];
B -- 否 --> D[执行 failureHandler];
C --> E[结束];
D --> E;
综上所述,闭包在 Swift 中具有强大的功能和广泛的应用场景,能够帮助我们编写更加灵活、可维护的代码。无论是结合数组算法、独立使用,还是用于改变类的功能和根据结果选择执行,闭包都展现出了其独特的优势。
Swift 闭包的使用与优势
6. 闭包在不同场景下的总结与对比
为了更清晰地了解闭包在不同场景下的应用,我们可以通过以下表格进行总结对比:
| 应用场景 | 代码示例 | 特点与优势 |
| ---- | ---- | ---- |
| 单语句闭包简化 |
let clos7 = { (first: Int, second: Int) -> Int in first + second }
| 省略
return
关键字,代码简洁,提高开发效率 |
| 结合数组算法 |
guests.map({print("Hello \($0)")})
| 可对数组元素进行批量处理,支持过滤、转换等操作 |
| 独立闭包 | 见
Guests
类和
getData
函数示例 | 分离数据与业务逻辑,便于创建可重用类,支持异步操作 |
| 改变功能 | 见
TestClass
和多个闭包示例 | 动态改变类的功能,增强代码灵活性 |
| 根据结果选择闭包 | 见
TestClass
的
isGreater
方法示例 | 根据不同条件执行不同闭包,实现复杂逻辑控制 |
7. 闭包使用的注意事项
在使用闭包时,我们还需要注意以下几点:
-
类型匹配
:闭包的参数类型和返回类型必须与使用场景相匹配,否则会导致编译错误。例如,在
TestClass
的
getNum
方法中,传递的闭包必须符合
getNumClosure
类型。
-
捕获变量
:闭包会捕获其定义上下文内的变量和常量,可能会导致内存泄漏。如果闭包持有对某个对象的强引用,而该对象又持有闭包的强引用,就会形成循环引用。可以使用弱引用(
weak
)或无主引用(
unowned
)来避免这种情况。
-
代码可读性
:虽然闭包可以使代码更简洁,但过度使用简写语法可能会降低代码的可读性。在编写闭包时,要根据实际情况选择合适的语法,确保代码易于理解和维护。
8. 闭包的进阶应用思路
闭包的应用不仅仅局限于上述场景,我们还可以进一步拓展其应用范围:
-
链式调用
:可以将多个闭包组合起来进行链式调用,实现更复杂的操作。例如,对数组元素先进行过滤,再进行转换,最后进行排序。
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let result = numbers.filter({ $0 % 2 == 0 }).map({ $0 * 2 }).sorted()
print(result)
- 自定义算法 :基于闭包,我们可以自定义一些复杂的算法。例如,实现一个自定义的排序算法,根据不同的比较规则对数组进行排序。
func customSort<T>(_ array: [T], by compare: (T, T) -> Bool) -> [T] {
var sortedArray = array
for i in 0..<sortedArray.count {
for j in i + 1..<sortedArray.count {
if compare(sortedArray[j], sortedArray[i]) {
sortedArray.swapAt(i, j)
}
}
}
return sortedArray
}
let unsortedNumbers = [5, 3, 8, 1, 2]
let sortedNumbers = customSort(unsortedNumbers) { $0 < $1 }
print(sortedNumbers)
9. 闭包在实际项目中的应用流程
在实际项目中,使用闭包的一般流程如下:
1.
需求分析
:确定项目中哪些部分可以使用闭包来实现,例如异步数据加载、动态功能切换等。
2.
闭包设计
:根据需求设计闭包的参数类型和返回类型,确保其与使用场景相匹配。
3.
代码实现
:编写闭包代码,并将其应用到相应的功能模块中。
4.
测试与优化
:对使用闭包的功能进行测试,检查是否存在内存泄漏、类型不匹配等问题,并进行优化。
以下是一个 mermaid 流程图,展示了闭包在实际项目中的应用流程:
graph LR;
A[需求分析] --> B[闭包设计];
B --> C[代码实现];
C --> D[测试与优化];
D --> E{是否满足需求?};
E -- 是 --> F[项目上线];
E -- 否 --> B;
通过以上对闭包的详细介绍和分析,我们可以看到闭包在 Swift 开发中具有重要的地位和广泛的应用前景。合理使用闭包可以使我们的代码更加灵活、高效、可维护,帮助我们更好地应对各种复杂的开发需求。
1107

被折叠的 条评论
为什么被折叠?



