调度组未按预期工作

Dispatch Groups not working as expected

我设置了一个与此类似的功能并且它工作正常,但是我在 group.leave() 上崩溃了,在测试中我注意到 1 或 2 个打印行 print("link = \(link)") 是在 print("getRekos processing over, handler is called") 之后打印,换句话说,group.leave() 在 for 循环完全完成之前被调用,或者一些迭代在组之外 "leaking"。是什么原因造成的?我已经发布了 getExpandedURL 的代码和我在 getRekos

中使用的自定义扩展 resolveWithCompletionHandler
 public func getRekos(rekoType: RekoCategory, handler: @escaping (Set<URL>) -> Void) {

        let setOfLinks = Set<URL>()
        let group = DispatchGroup() //Dispatch group code from: 
        let backgroundQ = DispatchQueue.global(qos: .default)


        TwitterRequest().fetchTweets(searchType: rekoType) { result in

            guard let tweets = TWTRTweet.tweets(withJSONArray: result) as? [TWTRTweet] else { print("error in getRekos casting TwitterRequest().fetchTweets result as [TWTRTweet]"); return }

            for tweet in tweets {

                let text = tweet.text

                group.enter()
                backgroundQ.async(group: group,  execute: {

                    //Check tweet text if contains any URLs
                    self.getExpandedURLsFromText(text: text, handler: { (getExpandedLinksResult, error) in

                        if error != nil {
                            group.leave()
                        } else {

                            for link in getExpandedLinksResult {
                                print("link = \(link)")

                            }
                            group.leave()
                        }
                    })
                })
            } //for tweet in tweets loop
            group.notify(queue: backgroundQ, execute: {
                DispatchQueue.main.async {
                    print("getRekos processing over, handler is called")
                    handler(setOfLinks)
                }
            })
        }
    }


private func getExpandedURLsFromText(text: String, handler: @escaping ([URL], Error?) -> Void) {

    var linksInText = [URL]()
    let group = DispatchGroup() //Dispatch group code from: 
    let backgroundQ = DispatchQueue.global(qos: .default)


    let types: NSTextCheckingResult.CheckingType = .link
    let detector = try? NSDataDetector(types: types.rawValue)

    guard let detect = detector else { print("NSDataDetector error"); return }

    let matches = detect.matches(in: text, options: .reportCompletion, range: NSMakeRange(0, (text.characters.count)))

    //CRITICAL CHECK - results were getting lost here, so pass back error if no match found
    if matches.count == 0 {
        handler([], RekoError.FoundNil("no matches"))
    }
        // Iterate through urls found in tweets
        for match in matches {

            if let url = match.url {

                    guard let unwrappedNSURL = NSURL(string: url.absoluteString) else {print("error converting url to NSURL");continue}

                    group.enter()
                    backgroundQ.async(group: group,  execute: {

                        //Show the original URL (ASYNC)
                        unwrappedNSURL.resolveWithCompletionHandler(completion: { (resultFromResolveWithCompletionHandler) in

                            guard let expandedURL = URL(string: "\(resultFromResolveWithCompletionHandler)") else {print("couldn't covert to expandedURL"); return}

                            linksInText.append(expandedURL)
                            group.leave()
                            //handler(linksInText, nil)
                        })
                    })


            } else { print("error with match.url") }
        } //for match in matches loop
            group.notify(queue: backgroundQ, execute: {
                DispatchQueue.main.async {
                    //print("getExpandedURLsFromText processing over, handler is called")
                    handler(linksInText, nil)
                }
            })
}

// Create an extension to NSURL that will resolve a shortened URL
extension NSURL
{
    func resolveWithCompletionHandler(completion: @escaping (NSURL) -> Void) {
        let originalURL = self
        let req = NSMutableURLRequest(url: originalURL as URL)
        req.httpMethod = "HEAD"

        URLSession.shared.dataTask(with: req as URLRequest){body, response, error in

            if error != nil {
                print("resolveWithCompletionHandler error = \(error)")
            }

            if response == nil {
                print("resolveWithCompletionHandler response = nil")
            }
            completion(response?.url as NSURL? ?? originalURL)
            }
            .resume()
    }
}

如果 getExpandedURLsFromText 中有多个匹配项 (matches.count > 1),则结果处理程序会被多次调用,从而使您的调度组逻辑中断。

清理你的逻辑。

您必须确保完成处理程序在成功时总是被调用一次,每次错误都被调用一次。您在 guard 语句中的多个位置缺少对完成处理程序的调用。