在for循环swift xcode之后未调用DispatchGroup.notify

时间:2019-03-02 18:43:00

标签: swift xcode grand-central-dispatch dispatch

我有一个for循环,可将结果从searchRequest递增到google。我使用DispatchGroup来确保在更新UI之前从searchRequest获取所有数据。但是,未调用我的dispatchGroup.notify函数,因此从未更新过我的UI。参见下面的代码:

func donationCenters(completion: @escaping ([DonationCenter])->()) {

    var placeSearchQuery = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(String(self.coordinates.latitude)),\(String(self.coordinates.longitude))&radius=1500&keyword=donation center&key=###########"

    // GROUP CREATED
    let myGroup = DispatchGroup()

    placeSearchQuery = placeSearchQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!

    var urlRequest = URLRequest(url: URL(string: placeSearchQuery)!)
    urlRequest.httpMethod = "GET"

    let task = URLSession.shared.dataTask(with: urlRequest) { (data, resopnse, error) in
        if error == nil {
            let jsonDict = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
            if let dict = jsonDict as? Dictionary<String, AnyObject> {
                if let results = dict["results"] as? [Dictionary<String, AnyObject>] {
                    var donationCenters: [DonationCenter] = []


                    for result in results {
                        myGroup.enter() // ENTER GROUP
                            let donationCenter = DonationCenter(name: text, image: image, latitude: location["lat"] as! Double, longitude: location["lng"] as! Double, phone: formattedPhoneNumber, website: website)
                            donationCenters.append(donationCenter)
                            myGroup.leave() // LEAVE GROUP
                    }


                    // NOTIFY - NEVER CALLED (done never printed)
                    myGroup.notify(queue: .main) {
                        print("done")
                        completion(donationCenters)
                    }
                }
            }
        } else {
            // Error with search request
        }
    }
    task.resume()
}

更新的代码:

func donationCenters(completion: @escaping ([DonationCenter])->()) {
    if let coordinates = self.coordinates {
        var placeSearchQuery = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(String(coordinates.latitude)),\(String(coordinates.longitude))&radius=1500&keyword=donation center&key=###########"
        placeSearchQuery = placeSearchQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
        var urlRequest = URLRequest(url: URL(string: placeSearchQuery)!)
        urlRequest.httpMethod = "GET"
        let task = URLSession.shared.dataTask(with: urlRequest) { (data, resopnse, error) in
            if error == nil {
                let jsonDict = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
                if let dict = jsonDict as? Dictionary<String, AnyObject> {
                    if let results = dict["results"] as? [Dictionary<String, AnyObject>] {
                        var donationCenters: [DonationCenter] = []
                        for result in results {
                            let text = result["name"] as! String
                            if let images = result["photos"] as? [Dictionary<String, AnyObject>] {
                                if let photoReference = images[0]["photo_reference"] as? String {
                                    self.imageForPhotoReference(photoReference, completion: { image in
                                        if let image = image {
                                            if let placeId = result["place_id"] as? String {
                                                var placeDetailsQuery = "https://maps.googleapis.com/maps/api/place/details/json?placeid=\(placeId)&fields=name,formatted_phone_number,website,formatted_address,geometry&key=########"
                                                placeDetailsQuery = placeDetailsQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
                                                var urlRequest = URLRequest(url: URL(string: placeDetailsQuery)!)
                                                urlRequest.httpMethod = "GET"
                                                let task = URLSession.shared.dataTask(with: urlRequest) { (data, resopnse, error) in
                                                    if error == nil {
                                                        let jsonDict = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
                                                        if let dict = jsonDict as? Dictionary<String, AnyObject> {
                                                            if let result = dict["result"] as? Dictionary<String, AnyObject> {
                                                                if let formattedPhoneNumber = result["formatted_phone_number"] as? String {
                                                                    if let website = result["website"] as? String {
                                                                        if let geometry = result["geometry"] as? Dictionary<String, AnyObject> {
                                                                            if let location = geometry["location"] as? Dictionary<String, AnyObject> {




                                                                                let donationCenter = DonationCenter(name: text, image: image, latitude: location["lat"] as! Double, longitude: location["lng"] as! Double, phone: formattedPhoneNumber, website: website)
                                                                                donationCenters.append(donationCenter)



                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    } else {
                                                        //we have error connection google api
                                                    }
                                                }
                                                task.resume()
                                            }
                                        }
                                    })
                                    // IF EMPTY RETURN NO REGISTERED DONATION CENTERS IN YOUR AREA
                                }
                            }
                        }
                        print("done")
                        completion(donationCenters)
                    }
                }
            } else {
                // Error with search request
            }
        }
        task.resume()
    }
}

2 个答案:

答案 0 :(得分:0)

要了解它的工作原理和使用方法,请先尝试简化代码,然后再扩展其功能。

让我们看看代码的这一部分

for result in results {
    myGroup.enter() // ENTER GROUP
    let donationCenter = DonationCenter(name: text, image: image, latitude: location["lat"] as! Double, longitude: location["lng"] as! Double, phone: formattedPhoneNumber, website: website)
    donationCenters.append(donationCenter)
    myGroup.leave() // LEAVE GROUP
 }

因为那里只有同步代码,所以它等同于

for result in results {
    myGroup.enter()
    myGroup.leave()
}

最后要

for result in results {}

如您所见,您所在的小组没有任何功能!

打开您的Playground,并尝试了解如何使用DispatchGroup进行需求(待完成所有背景任务后通知您)

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

// it mimics some function which do some job in the background
// as dataTask in your example
func asyncFoo(id: Int, completition: @escaping (_ result: String)->()) {
    let q = DispatchQueue(label: "internal", qos: .background, attributes: .concurrent)
    q.async {
        // running in the backgroud
        var sum  = 0
        for i in 1...1000 {
            let r = Int.random(in: 0..<i)
            sum += r
        }
        let res = sum.description
        completition(res)
    }
}

let group = DispatchGroup()

for i in 0..<10 {
    group.enter() // enter the group before the task starts
    asyncFoo(id: i) { (result) in
        print("id:", i, result)
        group.leave() // leave the group when task finished
    }
}
group.notify(queue: .main) {
    print("all done")
    PlaygroundPage.current.finishExecution()
}

print("continue execution ...")

它打印出类似的东西

continue execution ...    
id: 4 260320
id: 2 252045
id: 8 249323
id: 3 265640
id: 0 256478
id: 1 253038
id: 5 252521
id: 9 255435
id: 6 245125
id: 7 252262
all done

答案 1 :(得分:0)

在一些帮助下,我发现导致问题的原因是for循环。取而代之的是,我菊花链式链接了多个searchRequest,如下所示:

if results.count>0 {
            guard let placeId0 = results[0]["place_id"] as? String else { return }
            self.placeDetails(placeId0, completion: { (donationCenter0) in
                if results.count-1>0 {
                    guard let placeId1 = results[1]["place_id"] as? String else { return }
                    self.placeDetails(placeId1, completion: { (donationCenter1) in
                        if results.count-2>0 {
                            guard let placeId2 = results[2]["place_id"] as? String else { return }
                            self.placeDetails(placeId2, completion: { (donationCenter2) in
                                let donationCenters = [donationCenter0, donationCenter1, donationCenter2]
                                complete(donationCenters)
                            })
                        }
                    })
                }
            })
        }

然后在调用该函数时在completionHandler中,填充一个数组并更新我的UI。感谢大家的帮助。