在 Swift 中,如何在 GCD 主线程上调用带参数的方法?
Posted
技术标签:
【中文标题】在 Swift 中,如何在 GCD 主线程上调用带参数的方法?【英文标题】:In Swift how to call method with parameters on GCD main thread? 【发布时间】:2014-09-19 01:52:39 【问题描述】:在我的应用程序中,我有一个函数可以创建一个 NSRURLSession 并使用
发送一个 NSURLRequestsesh.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”。我将如何做到这一点,或者有人可以建议一种将方法调用添加到应用程序主队列的最佳方法?
【问题讨论】:
【参考方案1】:你可以切换到主线程使用
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()
【讨论】:
【参考方案2】:如果你在闭包中使用 self,不要忘记弱化 self。
dispatch_async(dispatch_get_main_queue(), [weak self] () -> () in
if let strongSelf = self
self?.doSomething()
)
【讨论】:
您能解释一下我们为什么要这样做吗? 这是因为它可以创建内存循环——即我对某事有强烈的引用,而它对我有强烈的引用。这意味着我们都不能离开内存堆。【参考方案3】:在主线程上重新加载collectionView
DispatchQueue.main.async
self.collectionView.reloadData()
【讨论】:
【参考方案4】:正确的方法是在 main_queue 中使用 dispatch_async,就像我在下面的代码中所做的那样
dispatch_async(dispatch_get_main_queue(),
(self.delegate as TBGQRCodeViewController).displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
)
【讨论】:
【参考方案5】:Swift 3+ & Swift 4 版本:
DispatchQueue.main.async
print("Hello")
Swift 3 和 Xcode 9.2:
dispatch_async_on_main_queue
print("Hello")
【讨论】:
【参考方案6】:现代版本的 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")
)
【讨论】:
虽然您的建议是正确的,但我认为我的回答稍微好一些,因为它不会调用 UIApplication.sharedApplication,这很不寻常,可能会影响我代码的其他读者。我的回答范围仅限于重要的对象,而您的回答则带来了辅助对象,这些对象需要我阅读更多文档才能准确了解我在做什么。我已经编辑了我原来的问题以包含正确的函数调用。我曾认为 displayQRCode 不够具体,但现在我们的 cmets 确实如此。感谢您指出这一点。【参考方案7】://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()
)
【讨论】:
【参考方案8】:斯威夫特 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
的外部范围内的闭包。
【讨论】:
这正是我正在寻找的 3 行......现在你可以停止阅读我的想法了【参考方案9】:这是更好的 (IMO) Swifty/Cocoa 样式语法,可实现与其他答案相同的结果:
NSOperationQueue.mainQueue().addOperationWithBlock(
// Your code here
)
或者您可以使用流行的Async Swift library 来获得更少的代码和更多的功能:
Async.main
// Your code here
【讨论】:
方法重命名为OperationQueue.main.addOperation(
【参考方案10】:
这是一个不错的小全局函数,您可以添加一个更好的语法:
func dispatch_on_main(block: dispatch_block_t)
dispatch_async(dispatch_get_main_queue(), block)
及用法
dispatch_on_main
// Do some UI stuff
【讨论】:
以上是关于在 Swift 中,如何在 GCD 主线程上调用带参数的方法?的主要内容,如果未能解决你的问题,请参考以下文章