在Swift中如何调用GCD主线程上的参数方法?

在我的应用程序中,我有一个函数,使一个nsururlsession和发送一个NSURLRequest使用

sesh.dataTaskWithRequest(req, completionHandler: {(data, response, error)

在这个任务的完成块中,我需要做一些计算,将一个UIImage添加到调用视图控制器中。我有一个func叫

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”添加到这个函数调用。我将如何做到这一点,或者有人可以建议一种向应用程序的主队列添加方法调用的最佳方法吗?

187855 次浏览

现代版本的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")
})

正确的方法是在main_queue中使用dispatch_async,就像我在下面的代码中所做的那样

dispatch_async(dispatch_get_main_queue(), {
(self.delegate as TBGQRCodeViewController).displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
})

如果你在闭包中使用self,不要忘记弱化self。

dispatch_async(dispatch_get_main_queue(),{ [weak self] () -> () in
if let strongSelf = self {
self?.doSomething()
}
})

斯威夫特2

使用尾随闭包就变成了:

dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}

尾随闭包是Swift的语法糖,允许在函数参数范围之外定义闭包。有关更多信息,请参阅Swift 2.2编程语言指南中的落后于闭包

在dispatch_async情况下,API是func dispatch_async(queue: dispatch_queue_t, _ block: dispatch_block_t),因为dispatch_block_t() -> Void的类型别名——一个接收0个参数且没有返回值的闭包,block是函数的最后一个参数,我们可以在dispatch_async的外部作用域中定义闭包。

下面是更好的(IMO) Swifty/Cocoa风格语法,可以实现与其他答案相同的结果:

NSOperationQueue.mainQueue().addOperationWithBlock({
// Your code here
})

或者你也可以使用流行的异步Swift库来获得更少的代码和更多的功能:

Async.main {
// Your code here
}

这里有一个不错的小全局函数,你可以添加一个更好的语法:

func dispatch_on_main(block: dispatch_block_t) {
dispatch_async(dispatch_get_main_queue(), block)
}

和使用

dispatch_on_main {
// Do some UI stuff
}

Swift 3+ &Swift 4版本:

DispatchQueue.main.async {
print("Hello")
}

Swift 3和Xcode 9.2:

dispatch_async_on_main_queue {
print("Hello")
}

在主线程上重新加载collectionView

DispatchQueue.main.async {
self.collectionView.reloadData()
}
//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()
})

您可以使用切换到主线程

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+),苹果建议将主线程任务交给main Actor,而不是GCD,以获得更干净、性能更好、更安全的代码。

我已经详细描述了使用actor分派到主线程的4种方法在这里

最简单的方法是使用@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")