【问题标题】:In Swift how to call method with parameters on GCD main thread?在 Swift 中,如何在 GCD 主线程上调用带参数的方法?
【发布时间】:2014-09-19 01:52:39
【问题描述】:

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

【问题讨论】:

    标签: ios swift cocoa-touch parameters grand-central-dispatch


    【解决方案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
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2011-08-02
                • 2014-08-19
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-08-04
                相关资源
                最近更新 更多