當需要實現類似“輪詢”這種操作的時候,我們可能會希望有一個“被放緩了的” for 循環 —— 即當某次操作執行后,稍等一下再去執行下一次操作。這時有幾種方法可以供我們使用,來實現這種效果,下面對比總結一下。
perform(_ aSelector: Selector, with anArgument: Any?, afterDelay delay: TimeInterval)
extension NSObject {
open func perform(_ aSelector: Selector, with anArgument: Any?, afterDelay delay: TimeInterval, inModes modes: [RunLoopMode])
open func perform(_ aSelector: Selector, with anArgument: Any?, afterDelay delay: TimeInterval)
open class func cancelPreviousPerformRequests(withTarget aTarget: Any, selector aSelector: Selector, object anArgument: Any?)
open class func cancelPreviousPerformRequests(withTarget aTarget: Any)
}
extension RunLoop {
open func perform(_ aSelector: Selector, target: Any, argument arg: Any?, order: Int, modes: [RunLoopMode])
open func cancelPerform(_ aSelector: Selector, target: Any, argument arg: Any?)
open func cancelPerformSelectors(withTarget target: Any)
}
這是系統提供的 API,其中最簡單的第 2 個方法可以通過指定方法、參數、延遲時間來實現延遲調用。
優點:使用簡單、自帶根據 target 取消調用的機制 并且可以指定對象、方法來取消延遲調用 使得這個方法可以完成一些復雜的延遲調用機制
缺點:系統幫你自動完成了很多操作,使得它不像定時器(NSTimer)那樣高度可控。
scheduledTimer(timeInterval ti: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any?, repeats yesOrNo: Bool) -> Timer
open class Timer : NSObject {
public /*not inherited*/ init(timeInterval ti: TimeInterval, invocation: NSInvocation, repeats yesOrNo: Bool)
open class func scheduledTimer(timeInterval ti: TimeInterval, invocation: NSInvocation, repeats yesOrNo: Bool) -> Timer
public /*not inherited*/ init(timeInterval ti: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any?, repeats yesOrNo: Bool)
open class func scheduledTimer(timeInterval ti: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any?, repeats yesOrNo: Bool) -> Timer
}
通過常見的定時器也可以實現延遲調用,把 repeats 參數置為 false,可以實現類似上面的 perform 的效果。
優點:定時器狀態可控,可以重新賦值,可以查看是否 valid 等
缺點:對比上面的 perform 方法可以直接傳參,這里想要傳參的話只能曲線救國:
- 把參數放在 timer 的 userInfo 里面,再把 timer 本身當成參數傳遞(這里使得代碼可讀性稍稍變差,但是好在 userInfo 里面什么都能放,這使得我們可以同時傳遞多個參數了)
- 如果你的代碼可以不兼容 iOS 10 以下的系統,新的 API 可以讓你不通過 selector 而是通過 block (closure) 執行代碼
asyncAfter(deadline: DispatchTime, qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, execute work: @escaping @convention(block) () -> Swift.Void)
DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now() + 1, execute: {
print("1 second after")
})
上面這段代碼,可以在 1 秒之后,通過主線程執行 print。值得注意的是,設定時間的時候,有兩種類型,一種是 DispatchTime,這個時間本質上是相對時間,它會在系統休眠的時候暫停,另外一種是 DispatchWallTime,這個時間正如其名,是“墻上的掛鐘時間”,是絕對時間,比如你可能希望某一個任務準確地在x小時、x分鐘、x秒之后執行,這段時間可能發生任何事情,這時推薦使用 DispatchWallTime.
神奇而偉大的 GCD 這里就不多做介紹了,有太多文章講解它了
優點:執行的是閉包,可以隨意傳參
缺點:一旦開啟,無法取消,只能提前在閉包里面添加嚴謹的邏輯判斷了