在我的应用程序中,我有一个创建 NSRURLSession 并使用发送 NSURLRequest 的函数
sesh.dataTaskWithRequest(req, completionHandler: {(data, response, error)
在此任务的完成块中,我需要进行一些计算,将 UIImage 添加到调用视图控制器。我有一个函数叫做
func displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
进行 UIImage 添加计算。如果我尝试在完成块内运行视图添加代码,Xcode 会抛出一个错误,提示我在后台进程中无法使用布局引擎。所以我在 SO 上找到了一些代码,它试图在主线程上排队一个方法:
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.0 * Double(NSEC_PER_MSEC)))
dispatch_after(time, dispatch_get_main_queue(), {
let returned = UIApplication.sharedApplication().sendAction("displayQRCode:", to: self.delegate, from: self, forEvent: nil)
})
但是,我不知道如何将参数“receiveAddr”和“amountBTC”添加到这个函数调用中。我将如何做到这一点,或者有人可以建议一种将方法调用添加到应用程序主队列的最佳方法?
现代版本的 Swift 使用 DispatchQueue.main.async
调度到主线程:
DispatchQueue.main.async {
// your code here
}
要在主队列上调度之后,请使用:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
// your code here
}
旧版本的 Swift 使用:
dispatch_async(dispatch_get_main_queue(), {
let delegateObj = UIApplication.sharedApplication().delegate as YourAppDelegateClass
delegateObj.addUIImage("yourstring")
})
Swift 3+ & Swift 4 版本:
DispatchQueue.main.async {
print("Hello")
}
斯威夫特 3 和 Xcode 9.2:
dispatch_async_on_main_queue {
print("Hello")
}
斯威夫特 2
使用尾随闭包,这变成:
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
Trailing Closures 是 Swift 语法糖,可以在函数参数范围之外定义闭包。有关更多信息,请参阅 Swift 2.2 编程语言指南中的 Trailing Closures。
在 dispatch_async 情况下,API 是 func dispatch_async(queue: dispatch_queue_t, _ block: dispatch_block_t)
,因为 dispatch_block_t
是 () -> Void
的类型别名 - 一个接收 0 个参数且没有返回值的闭包,并且 block 是函数的最后一个参数,我们可以在其中定义闭包dispatch_async
的外部范围。
在主线程上重新加载 collectionView
DispatchQueue.main.async {
self.collectionView.reloadData()
}
这是更好的 (IMO) Swifty/Cocoa 样式语法,可以实现与其他答案相同的结果:
NSOperationQueue.mainQueue().addOperationWithBlock({
// Your code here
})
或者,您可以使用流行的 Async Swift library 来获得更少的代码和更多的功能:
Async.main {
// Your code here
}
OperationQueue.main.addOperation({ }
正确的方法是在 main_queue 中使用 dispatch_async,就像我在下面的代码中所做的那样
dispatch_async(dispatch_get_main_queue(), {
(self.delegate as TBGQRCodeViewController).displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
})
这是一个不错的小全局函数,您可以添加一个更好的语法:
func dispatch_on_main(block: dispatch_block_t) {
dispatch_async(dispatch_get_main_queue(), block)
}
和用法
dispatch_on_main {
// Do some UI stuff
}
//Perform some task and update UI immediately.
DispatchQueue.global(qos: .userInitiated).async {
// Call your function here
DispatchQueue.main.async {
// Update UI
self.tableView.reloadData()
}
}
//To call or execute function after some time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
//Here call your function
}
//If you want to do changes in UI use this
DispatchQueue.main.async(execute: {
//Update UI
self.tableView.reloadData()
})
如果您在闭包内使用 self ,请不要忘记弱化 self 。
dispatch_async(dispatch_get_main_queue(),{ [weak self] () -> () in
if let strongSelf = self {
self?.doSomething()
}
})
您可以使用切换到主线程
DispatchQueue.main.async {
// UI Code Goes Here
}
您还可以通过以下 POP 编写更多可重用和可读的代码我为您编写自定义协议
protocol MainThreadRunAble : AnyObject {}
使用扩展实现协议
extension MainThreadRunAble {
func runOnMain(code : @escaping()->()) {
DispatchQueue.main.async {
code()
}
}
func runOnMain(withDelay delay : Float ,code : @escaping()->()){
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
code()
}
}}
使您的类符合您要在主线程上运行的协议
class YourClass : BaseClass,MainThreadRunAble{}
然后根据您的要求调用其中一种方法
runOnMain {
//update on main
}
runOnMain(withDelay: 1) {
//update on main
}
如果您使用任何架构并且只是想确保只有 viewcontroller 可以访问这段代码以在主线程上运行,然后实现您的扩展
extension UIViewController {
func runOnMain(code : @escaping()->()) {
DispatchQueue.main.async {
code()
}
}
func runOnMain(withDelay delay : Float ,code : @escaping()->()){
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
code()
}
}}
对于现代 Swift 代码(Swift 5.5+ 和 iOS 13+),Apple 建议将主线程任务交给 Main Actor 而不是 GCD,以获得更清洁、高性能和更安全的代码。
我已经详细概述了 4 种使用演员分派到主线程的方法here。
最简单的可能方法是使用 @MainActor
属性包装器对相关方法进行注释。
@MainActor func callFunctionOnMainThread(paramOne: Int, paramTwo: String) {
// We can now access parameters on the main thread
}
我们使用结构化并发,即async/await
:
await callFunctionOnMainThread(paramOne: 2, paramTwo: "Two")