2017-01-08 5 views
0

私はこれと同様の機能をセットアップしても問題ありませんが、ここではgroup.leave()にクラッシュしています。テストでは、print("getRekos processing over, handler is called")の後に1,つまり、group.leave()がforループを完全に完了する前に呼び出されているか、反復のいくつかがグループ外に「漏れている」ということです。これを引き起こす原因は何ですか?私は複数の一致(matches.count > 1が)getExpandedURLsFromTextである場合、結果ハンドラは、あなたのディスパッチグループ・ロジック・ブレークを行う、複数回呼び出されgetRekosディスパッチグループが期待通りに動作しない

public func getRekos(rekoType: RekoCategory, handler: @escaping (Set<URL>) -> Void) { 

     let setOfLinks = Set<URL>() 
     let group = DispatchGroup() //Dispatch group code from: http://stackoverflow.com/questions/38552180/dispatch-group-cannot-notify-to-main-thread 
     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: http://stackoverflow.com/questions/38552180/dispatch-group-cannot-notify-to-main-thread 
    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() 
    } 
} 
+0

私が気付くことができるのは、あなたが 'handler [(]、RekoError.FoundNil(" no matches "))'の後ろに 'return'しないということだけです。したがって、理論的には、 'handler(...) 'への2番目の呼び出しを置くことができます。しかし、あなたの場合は、 'match for matches'ループがトリガしないようにすべきではありません。 – shallowThought

+0

ありがとう、私はテストして、そのブロック(すなわち 'matches.count == 0 {')が決して起動しない場合、それはそれだとは思わないので – GarySabo

+0

'handler'が全く呼び出されないグループは決して通知しません。 – Sulthan

答えて

1

内部で使用されているgetExpandedURLためのコードと私のカスタム拡張resolveWithCompletionHandlerを掲載しましたアップ。

ロジックをクリーンアップします。

完了ハンドラは、正常終了時に常に1回、エラーごとに1回だけ呼び出されるようにする必要があります。 guardステートメントの複数の場所で完了ハンドラへの呼び出しがありません。

+0

あなたは何をお勧めしますか? 'getRekos'でやっていることと同様に' getExpandedURL'でディスパッチグループを使用していますか? – GarySabo

+0

@GarySaboそれはおそらく最も簡単な可能性の1つです。 – Sulthan

+0

'getExpandedURL'のディスパッチグループを使って私の質問を更新しました...これは私の元の質問を解決し、クラッシュせず、すべてのリンクが反復されたときに通知するだけですが、' getExpandedURL'の補完ハンドラはまだ呼び出されています何度も...私の '.enter'と' leave'コールをどのようにしてハンドラを一度呼び出せるようにすることができますか? – GarySabo

関連する問題