swift 3 DispatchGroup leave 在帮助类函数中调用时导致崩溃

Posted

技术标签:

【中文标题】swift 3 DispatchGroup leave 在帮助类函数中调用时导致崩溃【英文标题】:swift 3 DispatchGroup leave causes crash when called in helper class function 【发布时间】:2017-08-06 17:38:42 【问题描述】:

我正在使用 DispatchGroup.enter() 和 leave() 来处理帮助程序类的 reverseG 异步函数。问题很清楚,我正在使用 mainViewController 的对象在 helper 类中调用 mainViewControllers 的 dispatchGroup.leave() !有办法吗?

当在主视图控制器中声明 reverseG 时,同样的代码可以工作。

class Geo 
    var obj = ViewController()

    static func reverseG(_ coordinates: CLLocation, _ completion: @escaping (CLPlacemark) -> ()) 
        let geoCoder = CLGeocoder()
        geoCoder.reverseGeocodeLocation(coordinates)  (placemarks, error) in
            if let error = error 
                print("error: \(error.localizedDescription)")
            
            if let placemarks = placemarks, placemarks.count > 0 
                let placemark = placemarks.first!
                completion(placemark) // set ViewController's properties
             else 
                print("no data")
            
            obj.dispatchGroup.leave() // ** ERROR **
        
    



来自主视图控制器的函数调用

dispatchGroup.enter()
Geo.reverseG(coordinates, setValues) // completionHandler: setValues

dispatchGroup.notify(queue: DispatchQueue.main) 

    // call another function on completion


【问题讨论】:

每个leave 调用都必须有一个关联的enter 调用。如果您在没有先调用enter 的情况下调用leave,它将崩溃。这里的问题是您在某个组上调用enter,但reverseGViewController 的其他实例上调用leave。我建议将 DispatchGroup 作为参数传递给您的 reverseG 方法。或者,更好的是,reverseG 不应该离开组,而是将 leave 调用放在 reserveG 调用的完成处理程序中。 没错,已经做到了,而且成功了。谢谢 【参考方案1】:

每个leave 调用都必须有一个关联的enter 调用。如果您在没有先调用enter 的情况下调用leave,它将崩溃。这里的问题是您在某个组上调用enter,但reverseGViewController 的其他实例上调用leave。我建议将 DispatchGroup 作为参数传递给您的 reverseG 方法。或者,更好的是,reverseG 不应该离开组,而是将 leave 调用放在 reserveG 调用的完成处理程序中。

dispatchGroup.enter()
Geo.reverseG(coordinates)  placemark in
    defer  dispatchGroup.leave() 

    guard let placemark = placemark else  return 

    // use placemark here, e.g. call `setValues` or whatever


dispatchGroup.notify(queue: DispatchQueue.main) 
    // call another function on completion

class Geo 
    // var obj = ViewController()

    static func reverseG(_ coordinates: CLLocation, completion: @escaping (CLPlacemark?) -> Void) 
        let geoCoder = CLGeocoder()
        geoCoder.reverseGeocodeLocation(coordinates)  placemarks, error in
            if let error = error 
                print("error: \(error.localizedDescription)")
            
            completion(placemarks?.first)

            // obj.dispatchGroup.leave() // ** ERROR **
        
    


这将DispatchGroup 逻辑保持在应用程序的一个级别,使您的类不那么紧密耦合(例如,地理编码器不需要知道视图控制器是否使用调度组)。

坦率地说,如果只有一个电话,我不清楚您为什么要使用调度组。通常你会把你调用的任何东西放在完成处理程序中,进一步简化代码。如果您正在进行一系列呼叫,通常只使用组。 (也许你只是简化了你的代码 sn-p 而你实际上是在进行多次调用。在这种情况下,调度组可能是有意义的。但话又说回来,你不应该同时进行地理编码请求,这意味着一个完全不同的模式,完全一致。

【讨论】:

我只想在单独的类中反向地理函数,并在主类中使用地标来设置属性(不调用任何回调函数)。 function2 需要 function1 的结果,而 function3 需要 f1&f2 的结果,我使用完成处理程序仅设置主类属性。 f1 将设置要在 f2 中使用的属性,依此类推 带有完成处理程序,是不是像... f1(completion: f2(completion:f3)) ??如果我需要 f3 来使用 f1&f2 的结果 没有更多上下文很难说。鉴于我们已经在这里回答了调度组问题,我可能会建议您发布一个新问题,其中包含更广泛的背景,向我们展示您在做什么。如果您有可以运行的代码并且希望获得有关设计的反馈,那么codereview.stackexchange.com 可能是一个更好的论坛。或者,如果它不起作用,请继续在 Stack Exchange 上发布一个单独的问题。无论哪种方式,请随时使用指向其他问题的链接添加评论。但是我们已经偏离您最初的调度组问题太远了......【参考方案2】:

通过函数调用将 dispatchGroup 作为参数传递并且它起作用了。

Geo.reverseG(coordinates, dispatchGroup, setValues)

【讨论】:

【参考方案3】:

我的两分钱来展示如何工作: (也许对其他人有用..)

//  Created by ing.conti on 02/02/21.
//

import Foundation

print("Hello, World!")
let r = AsyncRunner()
r.runMultiple(args: ["Sam", "Sarah", "Tom"])





class AsyncRunner
    static let shared = AsyncRunner()
    
    let dispatchQueue = DispatchQueue(label: "MyQueue", qos:.userInitiated)
    let dispatchGroup = DispatchGroup.init()
    
    
    func runMultiple(args: [String])
        
        let count = args.count
        
        for i in 0..<count 
            
            dispatchQueue.async(group: dispatchGroup)  [unowned self] in
                dispatchGroup.enter()
                self.fakeTask(arg: args[i])
            
        
        
        _ = dispatchGroup.wait(timeout: DispatchTime.distantFuture)
    
    
    
    
    func fakeTask(arg: String)
        
        for i in 0..<3 
            print(arg, i)
            sleep(1)
        
        dispatchGroup.leave()
        

【讨论】:

以上是关于swift 3 DispatchGroup leave 在帮助类函数中调用时导致崩溃的主要内容,如果未能解决你的问题,请参考以下文章

Swift 伪原子并发同步代码引起 DispatchGroup.leave() 方法不平衡调用导致 App 崩溃的解决

Swift 伪原子并发同步代码引起 DispatchGroup.leave() 方法不平衡调用导致 App 崩溃的解决

- swift:async + JSON + completion + DispatchGroup

使用 DispatchGroup、DispatchQueue 和 DispatchSemaphore 按顺序执行带有 for 循环的 Swift 4 异步调用

在 Swift 中的 Firebase 实时数据库观察方法中具有异步函数的 DispatchGroup

通过 DispatchGroup 与 DispatchQueue 访问主队列