我正在使用Swift。
使用dispatch_after
块通常比使用sleep(time)
更好,因为执行sleep
操作的线程会被阻塞,无法执行其他工作。使用dispatch_after
时,被处理的线程不会被阻塞,因此可以在此期间执行其他工作。
如果您正在处理应用程序的主线程,则使用sleep(time)
会影响用户体验,因为在此期间UI会变得不响应。
Dispatch after
会安排执行一段代码块,而不会冻结线程:
let seconds = 4.0
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
// Put your code which should be executed with a delay here
}
func foo() async {
try await Task.sleep(nanoseconds: UInt64(seconds * Double(NSEC_PER_SEC)))
// Put your code which should be executed with a delay here
}
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), 4 * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
// Put your code which should be executed with a delay here
}
.now() + .seconds(4)
会出现错误:expression type is ambiguous without more context
。 - richards.now() + .seconds(5)
,直接使用.now() + 5
即可。 - cnzac如果你需要在UI线程中实现延迟操作,不要使用sleep方法,因为它会阻塞程序。而应该考虑使用NSTimer
或者dispatch timer。
但是,如果你真的需要在当前线程中实现延迟操作:
do {
sleep(4)
}
这里使用了UNIX中的sleep
函数。
在Swift 3.0中不同方法的比较
1. Sleep
这种方法没有回调函数。将代码直接放在此行后,在4秒钟后执行。它会阻止用户与UI元素(如测试按钮)交互,直到时间结束。虽然当睡眠开始时,该按钮有点冻结,但其他元素(如活动指示器)仍在旋转而不会冻结。在睡眠期间,您无法再次触发此操作。
sleep(4)
print("done")//Do stuff here
2. Dispatch, Perform and Timer
这三种方法的工作方式类似,它们都在后台线程上运行,并带有回调函数,只是语法和功能略有不同。
Dispatch通常用于在后台线程上运行某些操作。它将回调作为函数调用的一部分。
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
print("done")
})
Perform是一个简化的计时器。它设置了一个带有延迟的计时器,然后通过选择器触发功能。
perform(#selector(callback), with: nil, afterDelay: 4.0)
func callback() {
print("done")
}}
最后,定时器还提供了重复调用回调函数的能力,但在这种情况下并不实用。
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(callback), userInfo: nil, repeats: false)
func callback() {
print("done")
}}
对于这三种方法,在点击按钮触发它们时,UI不会冻结,您可以再次单击它。如果您再次单击按钮,则会设置另一个计时器并触发回调两次。
总结
这四种方法没有一种单独表现得足够好。 sleep
将禁用用户交互,因此屏幕会"冻结"(实际上并非如此),导致用户体验不佳。其他三种方法不会冻结屏幕,但您可能会多次触发它们,并且大多数情况下,您希望在允许用户再次调用之前等待收到回调。
因此,更好的设计将使用其中一种具有屏幕阻塞功能的异步方法。当用户单击按钮时,用一些半透明视图覆盖整个屏幕,并在顶部放置旋转活动指示器,告诉用户正在处理按钮单击。然后在回调函数中删除视图和指示器,告诉用户操作已经正确处理等等。
@objc func callback() {
- leanne在Swift 4.2和Xcode 10.1中,您总共有4种延迟的方式。其中,选项1是推荐的,在一段时间后调用或执行一个函数。使用sleep()是最不常见的情况。
选项1。
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.yourFuncHere()
}
//Your function here
func yourFuncHere() {
}
选项 2。
perform(#selector(yourFuncHere2), with: nil, afterDelay: 5.0)
//Your function here
@objc func yourFuncHere2() {
print("this is...")
}
选项3。
Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(yourFuncHere3), userInfo: nil, repeats: false)
//Your function here
@objc func yourFuncHere3() {
}
选项4。
sleep(5)
如果想在一段时间后调用函数以执行某些操作,请勿使用sleep。
我同意 Palle 的观点,使用 dispatch_after
在这里是一个不错的选择。但你可能不喜欢 GCD 调用,因为它们写起来相当让人烦恼。相反,你可以添加这个方便的帮助程序:
public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void) {
let dispatchTime = DispatchTime.now() + seconds
dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}
public enum DispatchLevel {
case main, userInteractive, userInitiated, utility, background
var dispatchQueue: DispatchQueue {
switch self {
case .main: return DispatchQueue.main
case .userInteractive: return DispatchQueue.global(qos: .userInteractive)
case .userInitiated: return DispatchQueue.global(qos: .userInitiated)
case .utility: return DispatchQueue.global(qos: .utility)
case .background: return DispatchQueue.global(qos: .background)
}
}
}
现在,您只需像这样在后台线程上延迟执行您的代码:
现在,您只需要像这样在后台线程上延迟执行您的代码:
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
在主线程上延迟代码甚至更加简单:
delay(bySeconds: 1.5) {
// delayed code, by default run in main thread
}
如果您更喜欢一个框架,它还有一些更方便的功能,请查看HandySwift。 您可以通过SwiftPM将其添加到项目中,然后像上面的示例一样使用:
import HandySwift
delay(by: .seconds(1.5)) {
// delayed code
}
DispatchTime
不可用。之前是let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
。 - Jeehut你也可以使用Swift 3来完成这个操作。
像这样延迟执行函数。
override func viewDidLoad() {
super.viewDidLoad()
self.perform(#selector(ClassName.performAction), with: nil, afterDelay: 2.0)
}
@objc func performAction() {
//This function will perform after 2 seconds
print("Delayed")
}
@nneonneo的答案建议使用NSTimer
,但没有展示如何实现。这是基本语法:
let delay = 0.5 // time in seconds
NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(myFunctionName), userInfo: nil, repeats: false)
这是一个非常简单的项目,展示了如何使用它。当按下按钮时,它会启动一个计时器,在半秒钟延迟后调用一个函数。
import UIKit
class ViewController: UIViewController {
var timer = NSTimer()
let delay = 0.5
// start timer when button is tapped
@IBAction func startTimerButtonTapped(sender: UIButton) {
// cancel the timer in case the button is tapped multiple times
timer.invalidate()
// start the timer
timer = NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false)
}
// function to be called after the delay
func delayedAction() {
print("action has started")
}
}
使用dispatch_time
(例如Palle的答案中所述)是另一个有效的选项。但是,它很难以取消。使用NSTimer
,在延迟事件发生之前取消它,你只需要调用
timer.invalidate()
使用 sleep
不被推荐,特别是在主线程上,因为它会停止该线程上所有正在执行的工作。
关于更详细的解答,请参考这里。
尝试使用Swift 3.0中的以下实现:
func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
completion()
}
}
使用方法
delayWithSeconds(1) {
//Do something
}
您可以轻松创建使用延迟函数的扩展(语法:Swift 4.2+)
extension UIViewController {
func delay(_ delay:Double, closure:@escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}
}
如何在UIViewController中使用
self.delay(0.1, closure: {
//execute code
})