ChatGPT解决这个技术问题 Extra ChatGPT

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

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


p
pkamb

现代版本的 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 不够具体,但现在有了我们的评论。感谢您指出了这一点。
p
pkamb

Swift 3+ & Swift 4 版本:

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

斯威夫特 3 和 Xcode 9.2:

dispatch_async_on_main_queue {
    print("Hello")
}

M
Maxim Veksler

斯威夫特 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 行……现在你可以停止阅读我的想法了
p
pkamb

在主线程上重新加载 collectionView

DispatchQueue.main.async {
    self.collectionView.reloadData()
}

p
pejalo

这是更好的 (IMO) Swifty/Cocoa 样式语法,可以实现与其他答案相同的结果:

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

或者,您可以使用流行的 Async Swift library 来获得更少的代码和更多的功能:

Async.main {
    // Your code here
}

方法重命名为 OperationQueue.main.addOperation({ }
p
pkamb

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

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

M
Mateusz Grzegorzek

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

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

和用法

dispatch_on_main {
    // Do some UI stuff
}

N
Naresh
//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()
})

p
pkamb

如果您在闭包内使用 self ,请不要忘记弱化 self 。

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

你能解释一下为什么我们应该这样做吗?
这是因为它可以创造记忆周期——即我对某事有强烈的参考,而它对我有强烈的参考。这意味着我们都不能离开内存堆。
S
Saifullah ilyas

您可以使用切换到主线程

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()
    }
}}

P
Pranav Kasetti

对于现代 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")