swift 3 DispatchGroup leave causes crash when called in helper class function
Asked Answered
D

3

11

I'm using DispatchGroup.enter() and leave() to process a helper class's reverseG async function. Problem is clear, I'm using mainViewController's object to call mainViewControllers's dispatchGroup.leave() in helper class! Is there a way to do it?

Same code works when reverseG is declared in the main view controller.

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 **
        }
    }


}

Function call from main view controller

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

dispatchGroup.notify(queue: DispatchQueue.main) {

    // call another function on completion

}
Dearly answered 6/8, 2017 at 17:38 Comment(2)
Every leave call must have an associated enter call. If you call leave without having first called enter, it will crash. The issue here is that you're calling enter on some group, but reverseG is calling leave on some other instance of ViewController. I'd suggest passing the DispatchGroup as a parameter to your reverseG method. Or, better, reverseG shouldn't leave the group, but rather put the leave call inside the completion handler that reserveG calls.Jeffereyjefferies
Exactly, already have done that and it worked. ThanksDearly
J
12

Every leave call must have an associated enter call. If you call leave without having first called enter, it will crash. The issue here is that you're calling enter on some group, but reverseG is calling leave on some other instance of ViewController. I'd suggest passing the DispatchGroup as a parameter to your reverseG method. Or, better, reverseG shouldn't leave the group, but rather put the leave call inside the completion handler that reserveG calls.

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
}

And

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 **
        }
    }

}

This keeps the DispatchGroup logic at one level of the app, keeping your classes less tightly coupled (e.g. the Geo coder doesn't need to know whether the view controller uses dispatch groups or not).

Frankly, I'm not clear why you're using dispatch group at all if there's only one call. Usually you'd put whatever you call inside the completion handler, simplifying the code further. You generally only use groups if you're doing a whole series of calls. (Perhaps you've just simplified your code snippet whereas you're really doing multiple calls. In that case, a dispatch group might make sense. But then again, you shouldn't be doing concurrent geocode requests, suggesting a completely different pattern, altogether.

Jeffereyjefferies answered 6/8, 2017 at 17:52 Comment(3)
I just wanted reverse geo functions in separate class and use placemarks in main class to set properties(not call any callback function). function1's result is needed for function2 and function3 needs results from f1&f2 and I'm using completion handlers to only set main class properties. f1 will set property to be used in f2 and so onDearly
with completion handlers, is it like... f1(completion: f2(completion:f3)) ?? if I need f3 to use results of f1&f2Dearly
It's hard to say without more context. Given that we've answered the dispatch group question here, I might suggest you post a new question with a broader context showing us what you're doing. If you've got code that's working and you want feedback on the design, perhaps codereview.stackexchange.com is a better forum. Or if it's not working, go ahead and post a separate question on Stack Exchange. Either way, feel free to add comment with the link to that other question. But we've already strayed too far from your original dispatch group question...Jeffereyjefferies
D
0

Passed dispatchGroup as parameter with function call and it worked.

Geo.reverseG(coordinates, dispatchGroup, setValues)
Dearly answered 6/8, 2017 at 17:53 Comment(0)
G
0

my two cents to show how can work: (maybe useful for others..)

//  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()
    }    
}
Guessrope answered 2/2, 2021 at 18:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.