How to notify a queue in Swift (GCD)

对着背影说爱祢 提交于 2019-12-20 06:39:53

问题


I'm using GCD to notify main thread (have 2 async calls inside the function)

My code:

func getWavesByMostRecent(closure: @escaping ([Wave]?) -> Void) {
    var waves = [Wave]()
    let dispatchGroup = DispatchGroup()

    self.query = DatabaseManager.waveRef.queryOrdered(byChild: Constants.reverseTimeStampKey)
    self.handle = self.query?.observe(.value, with: { (snapshot) in

        for value in snapshot.children {
            guard let wave = Wave(snapshot: value as! DataSnapshot) else { return }

            self.geoFire = GeoFire(firebaseRef: DatabaseManager.waveRef)
            let currentLocation = LocationManager.shared.getCurrentLocation()

            dispatchGroup.enter()
            self.geoFire?.getLocationForKey(wave.waveID, withCallback: { (location, error) in
                guard let location = location else { return }
                if error == nil {
                    if location.distance(from: currentLocation) < Constants.distance {
                        print("Wave", wave.waveID, "is in range")
                        waves.append(wave)
                    } else {
                        print("Wave", wave.waveID, "is out of range")
                    }
                } else {
                    print(error?.localizedDescription ?? "")
                }
                dispatchGroup.leave()
            })
        }
        dispatchGroup.notify(queue: .main) {
            print("THERE ARE SO MANY WAVES:", waves.count)
            closure(waves)
        }
    })
}

But .notify closure just doesn't work and I cannot call my "main" closure right. What am I doing wrong? Any advice would be appreciated.


回答1:


Try this change:

self.geoFire?.getLocationForKey(wave.waveID, withCallback: { (location, error) in
    defer { dispatchGroup.leave() }
    guard let location = location else { return }
    if error == nil {
        if location.distance(from: currentLocation) < Constants.distance {
            print("Wave", wave.waveID, "is in range")
            waves.append(wave)
        } else {
            print("Wave", wave.waveID, "is out of range")
        }
    } else {
        print(error?.localizedDescription ?? "")
    }
})

As noted in matt's comment defer is a good tool to do something always when leaving.


This is another issue, but updating an Array from multiple thread simultaneously would cause some problems. It rarely happens, so it can be a hard-to-fix bug.

I'm not sure if GeoFire calls its callback in the main thread or not, but if not, you should better enclose all the callback code in DispatchQueue.main.async {...}.




回答2:


dispatchGroup.leave() is still in the closure, instead it should be at the end of the for loop like this:

func getWavesByMostRecent(closure: @escaping ([Wave]?) -> Void) {
    var waves = [Wave]()
    let dispatchGroup = DispatchGroup()

    self.query = DatabaseManager.waveRef.queryOrdered(byChild: Constants.reverseTimeStampKey)
    self.handle = self.query?.observe(.value, with: { (snapshot) in

        for value in snapshot.children {
            guard let wave = Wave(snapshot: value as! DataSnapshot) else { return }

            self.geoFire = GeoFire(firebaseRef: DatabaseManager.waveRef)
            let currentLocation = LocationManager.shared.getCurrentLocation()

            dispatchGroup.enter()
            self.geoFire?.getLocationForKey(wave.waveID, withCallback: { (location, error) in
                guard let location = location else { return }
                if error == nil {
                    if location.distance(from: currentLocation) < Constants.distance {
                        print("Wave", wave.waveID, "is in range")
                        waves.append(wave)
                    } else {
                        print("Wave", wave.waveID, "is out of range")
                    }
                } else {
                print(error?.localizedDescription ?? "")
                }
            })
            dispatchGroup.leave()
        }
        dispatchGroup.notify(queue: .main) {
            print("THERE ARE SO MANY WAVES:", waves.count)
            closure(waves)
        }
    })
}


来源:https://stackoverflow.com/questions/51349678/how-to-notify-a-queue-in-swift-gcd

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!