Swift 可选的逃逸闭包参数
在 Swift 中,可选的逃逸闭包参数是指在传递给它的函数返回后可以执行的闭包。在本文中,我们将通过示例了解如何创建逃逸闭包作为参数。
要声明可选的逃逸闭包参数,请在函数参数列表中的闭包类型前添加 @escaping 关键字。
语法
语法如下。
func doSomething(completion: @escaping () -> Void) {
// 在此处编写代码
}
在上面的代码中,completion 是一个可选的逃逸闭包参数,它不接受任何参数并返回 Void。
@escaping 关键字表示闭包可以在 doSomething 函数返回后执行,这意味着闭包将被存储以供将来执行。这在使用闭包进行异步操作(例如网络请求或动画)的场景中非常有用。
请注意,如果将闭包传递给带有可选逃逸闭包参数的函数,则在引用闭包内部的属性或方法时,需要使用 self. 前缀明确指示允许该闭包逃逸。这是因为闭包可能会在函数返回后执行,因此闭包需要捕获对 self 的强引用,以确保引用的属性或方法仍然有效。
以下示例演示了如何在 Swift 中使用可选逃逸闭包
步骤 1 - 在此步骤中,NetworkManager 类有一个名为 fetchDataFromServer 的函数,它接受一个名为 completionHandler 的可选逃逸闭包参数。此闭包接受一个字符串数组作为参数并返回 Void。
class NetworkManager {
func fetchDataFromServer(completionHandler: @escaping ([String]) -> Void) {
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 2) {
let data = ["John", "Sarah", "Jane"]
completionHandler(data)
}
}
}
在 fetchDataFromServer 内部,闭包会被存储以供稍后执行,并在延迟 2 秒后异步执行。然后,数据会作为参数传递给闭包。
步骤 2 - 在 TestController 中,dataArray 属性会使用从闭包接收的数据进行更新,然后在主线程上使用新数据更新 UI。
请注意,我们使用 [weak self] 捕获列表来避免 TestController 和闭包之间出现强引用循环。这是因为闭包是异步执行的,如果在闭包执行之前释放 TestController,闭包仍然会持有对 TestController 的强引用,从而阻止其被释放。
示例
class TestController: UIViewController {
private lazy var clickButton: UIButton = {
let button = UIButton()
button.addTarget(self, action: #selector(handleButtonClick), for: .touchUpInside)
button.backgroundColor = .darkGray
button.setTitle("Fetch Data", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
button.layer.cornerRadius = 5
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
var networkManager = NetworkManager()
var dataArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
initialSetup()
}
private func initialSetup() {
view.backgroundColor = .white
navigationItem.title = "Escaping Closure"
view.addSubview(clickButton)
clickButton.widthAnchor.constraint(equalToConstant: 200).isActive = true
clickButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
clickButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
clickButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
@objc private func handleButtonClick() {
}
}
输出

步骤 3 − 通过调用 fetchDataFromServer() 方法完成 handleButtonClick() 的实现。代码如下 −
示例
@objc private func handleButtonClick() {
networkManager.fetchDataFromServer { [weak self] data in
self?.dataArray = data
DispatchQueue.main.async {
print("Data fetched: \(data)")
self?.clickButton.setTitle("Data Fetched", for: .normal)
}
}
}
输出
Data fetched: ["John", "Sarah", "Jane"]
在上面的例子中,我们定义了一个 NetworkManager 类,其中包含一个名为 fetchDataFromServer 的函数,该函数接受一个名为 completionHandler 的可选转义闭包参数。在 fetchDataFromServer 内部,我们使用 DispatchQueue.global.asyncAfter 模拟了 2 秒的延迟,然后将一些数据(一个字符串数组)作为参数传递给该闭包。
程序运行时,我们等待 2 秒,然后执行传递给 fetchDataFromServer 的闭包。该闭包使用接收到的数据更新 dataArray 属性,然后更新 UI。
另一个示例
此示例展示了如何在 Swift 中使用可选转义闭包 -
class MyData {
var data = [1, 2, 3, 4, 5]
func fetchData(completion: @escaping ([Int]) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
completion(self.data)
}
}
}
let myData = MyData()
myData.fetchData { (data) in
print("Data received: \(data)")
}
print("Waiting for data...")
输出
Waiting for data... Data received: [1, 2, 3, 4, 5]
在上面的代码中,我们定义了一个 MyData 类,其中包含一个名为 fetchData 的函数,该函数接受一个名为 completion 的可选转义闭包参数。在 fetchData 内部,我们使用 DispatchQueue.main.asyncAfter 模拟 1 秒的延迟,然后将数据数组作为参数传递给闭包。
程序运行时,它首先打印"Waiting for data...",然后等待 1 秒,再执行传递给 fetchData 的闭包。该闭包将"Data received: [1, 2, 3, 4, 5]"打印到控制台。
结论
总而言之,可选转义闭包是 Swift 的一个强大功能,它使我们能够以简洁高效的方式处理异步代码。使用可选转义闭包,我们可以将函数作为参数传递给其他函数,并在稍后执行它们,即使在调用函数返回之后也是如此。这使得编写兼具可读性和可维护性的异步代码变得容易。
可选转义闭包可用于各种场景,例如网络请求、动画和用户界面更新。通过使用可选转义闭包,我们可以编写兼具高性能和响应能力的代码,而不会牺牲可读性和可维护性。