Swift 中,咱们能够运用 @escaping 关键字来界说逃逸闭包。逃逸闭包是指闭包在函数返回后才被调用的闭包。这种闭包一般作为异步API的回调参数,用于在异步操作完成后履行一些代码。

逃逸闭包与非逃逸闭包

闭包作为函数的参数能够是被传递和运用的。当一个闭包被界说为函数的参数时,它默以为非逃逸闭包,即它有必要在函数履行完毕前调用。可是,当一个闭包在函数返回后依然被调用时,咱们称之为逃逸闭包

运用场景

异步API的回调参数

例如,在运用URLSession进行网络恳求时,咱们能够运用逃逸闭包来处理异步恳求的结果:

func loadData(completionHandler: @escaping (Data?, Error?) -> Void) {
    let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
        completionHandler(data, error)
    }
    task.resume()
}

推迟履行的操作

例如,咱们能够运用逃逸闭包来完成一些推迟履行的操作,例如在一些情况下咱们需求在弹出视图控制器之后履行一些操作:

class MyClass {
    // 默以为逃逸闭包
    var completionHandler: (() -> Void)?
    func doSomething() {
        // 异步操作
        completionHandler?()
    }
}
let myObject = MyClass()
myObject.completionHandler = {
    print("操作完成")
}
myObject.doSomething()

在这个比如中,咱们将界说一个completionHandler闭包,该闭包将在视图控制器被弹出后履行。咱们能够运用逃逸闭包来完成这个功能。

当一个闭包被界说为函数的参数时,默以为非逃逸闭包
当一个闭包被界说为某个类的变量时,默以为逃逸闭包

定时器

有时候,咱们需求开启一个定时器,然后在定时器完毕后履行某些操作。运用逃逸闭包能够方便地完成这样的需求。例如:

class TimerManager {
    func startTimer(duration: TimeInterval, completionHandler: @escaping () -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
            completionHandler()
        }
    }
}
let timerManager = TimerManager()
timerManager.startTimer(duration: 2) {
    print("定时器完毕!")
}

在这个比如中,咱们界说了一个TimerManager类,并在其间声明晰一个逃逸闭包completionHandler
当咱们调用startTimer办法时,它会开启一个计时器并在指定的时间后调用completionHandler

动画

当咱们需求在一段时间内履行一些动画操作时,能够运用逃逸闭包完成。例如:

class AnimationManager {
    func animate(duration: TimeInterval, completionHandler: @escaping () -> Void) {
        UIView.animate(withDuration: duration) {
            // 履行动画
        } completion: { finished in
            completionHandler()
        }
    }
}
let animationManager = AnimationManager()
animationManager.animate(duration: 1) {
    print("动画完毕!")
}

在这个比如中,咱们界说了一个AnimationManager类,并在其间声明晰一个逃逸闭包completionHandler。当咱们调用animate办法时,它会履行一些动画操作,并在动画完成后调用completionHandler进行处理。

运用陷阱

防止循环引证

逃逸闭包捕获了self或其它目标时,或许会产生循环引证的问题。假如不处理这个问题,会导致内存泄漏和应用程序的崩溃。为了防止循环引证,咱们需求运用一个弱引证或无主引证来捕获self。例如:

class MyClass {
    var completionHandler: (() -> Void)?
    func doSomething() {
        someAsyncOperation { [weak self] in
            self?.completionHandler?()
        }
    }
}

在这个比如中,咱们运用了一个弱引证self,防止了循环引证问题。当逃逸闭包被调用时,咱们运用self?.completionHandler?()来访问completionHandler属性,由于此刻self或许现已被释放了

总结

Swift中,@escaping关键字用于界说逃逸闭包,逃逸闭包一般作为异步API的回调参数运用。运用逃逸闭包有许多优点

  1. 异步API的回调参数有必要是逃逸闭包。由于异步API履行时间较长,非逃逸闭包或许会在函数返回之前就被销毁。因此,逃逸闭包能够确保在异步操作完成后一定会履行
  2. 在某些情况下,闭包需求在函数外部被调用。例如,假如咱们要将一个闭包作为一个数组的元素,那么闭包有必要是逃逸闭包
  3. 逃逸闭包能够使代码更加简洁和易于办理。当一个函数需求异步履行时,咱们不需求创建一个新的类或目标来办理异步操作的结果,而是能够运用逃逸闭包来处理它们