DispatchQueue.main.async 块中未更新完成处理程序的返回值

Posted

技术标签:

【中文标题】DispatchQueue.main.async 块中未更新完成处理程序的返回值【英文标题】:return value from completion handler is not updated in DispatchQueue.main.async block 【发布时间】:2018-12-29 17:19:11 【问题描述】:

我正在从一个类调用一个带有完成处理程序的函数到另一个类

调用类:

class PVClass


var avgMonthlyAcKw:Double = 0.0

var jsonString:String!

func estimateMonthlyACkW (areaSqFt:Float, completion: @escaping(Double) -> () )

var capacityStr:String = ""

let estimatedCapacity = Float(areaSqFt/66.0)
capacityStr = String(format: "%.2f", estimatedCapacity)

// Build some Url string
var urlString:String = "https://developer.nrel.gov/"
urlString.append("&system_capacity=")
urlString.append(capacityStr)

let pvURL = URL(string: urlString)
let dataTask = URLSession.shared.dataTask(with: pvURL!)  data, response, error in
    do 

        let _ = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
        self.jsonString = String(data: data!, encoding: .utf8)!
        print("JSON String:\(String(describing: self.jsonString))")

        if self.jsonString != nil 
            let decoder = JSONDecoder()
            let jsonData = try decoder.decode(PVClass.Top.self, from: data!)

            // do some parsing here
            var totalAcKw: Double = 0.0
            let cnt2: Int = (jsonData.Outputs?.ACMonthly.count)!
            for i in 0..<(cnt2-1) 
                totalAcKw = totalAcKw + (jsonData.Outputs?.ACMonthly[i])!
            
            self.avgMonthlyAcKw = Double(totalAcKw)/Double(cnt2)

            // prints value
            print("updated estimate: ", self.avgMonthlyAcKw)
           completion(self.avgMonthlyAcKw)
        

     catch 
        print("error: \(error.localizedDescription)")

    

dataTask.resume()


调用类:

  aPVClass.estimateMonthlyACkW(areaSqFt: 100.0, completion:  (monthlyAckW) -> Void in

        DispatchQueue.main.async  [weak self] in
            guard case self = self else 
                return
            

            print("monthlyAckW: ", monthlyAckW)
            self?.estimatedSolarkWh = Int(monthlyAckW * Double((12)/365 * (self?.numDays)!))
            print("estimatedSolarkWh: ", self?.estimatedSolarkWh ?? 0)
            guard let val = self?.estimatedSolarkWh  else  return 
            print("val: ", val)
            self?.estimatedSolarkWhLabel.text = String(val)
            self?.view.setNeedsDisplay()
        

    )

 

monthlyAckW 在完成处理程序返回后具有正确的值。但是分配给 self?.estimatedSolarkWh 的值为 0,值永远不会转移到当前类范围,UI 更新失败,即使在 DispatchQueue.main.async 之后 请问这个怎么解决?

【问题讨论】:

【参考方案1】:

completion 打错地方了。将其移到打印行之后的数据任务的完成闭包中

    // prints value
    print("updated estimate: ", self.avgMonthlyAcKw)
    completion(self.avgMonthlyAcKw)

恢复后删除

dataTask.resume()completion(self.avgMonthlyAcKw)

【讨论】:

我这样做了,但结果是一样的(实际上在我的代码中我已经尝试过,但是在发布代码时我搞砸了,所以我按照你的建议更新了代码)。但错误仍然相同 - 在完成处理程序返回后,monthlyAckW 具有正确的值。但是给 self?.estimatedSolarkWh 赋值为 0。返回值不能赋值给调用类作用域 在闭包内调用 completion 应该可以工作。但是 GCD 闭包中的这种奇怪的weak self guard 舞蹈是无稽之谈。 DispatchQueue 闭包不会导致保留周期。 我确实尝试过没有弱自我等,但它从来没有奏效。缺少一些东西,我无法弄清楚。花了几个小时 数据任务闭包中的print 行是否显示任何内容? All 调用代码打印中的打印行。在调用处理程序时,只有返回值monthlyAckW 才能正确打印。分配了值的 var self?.estimatedSolarkWh 不会打印分配的值,而是打印变量 estimatedSolarkWh 的默认初始值。这是打印输出:monthlyAckW: 340.928085327148estimateSolarkWh: 0 val: 0

以上是关于DispatchQueue.main.async 块中未更新完成处理程序的返回值的主要内容,如果未能解决你的问题,请参考以下文章

为啥 DispatchQueue.main.async 会影响单元格自动调整大小?

SwiftUI - HealthKit 中的 DispatchQueue.main.async

SwiftUI - HealthKit 中的 DispatchQueue.main.async

Swift 5:我无法让我的 UITableView 及时更新(同时使用 `DispatchQueue.global().sync` 和 `DispatchQueue.main.async`

在 DispatchQueue.main.async 上运行代码是不是更慢?

DispatchQueue.main.async 的初学者问题