2017-01-08 4 views
0

비슷한 기능을 설정했지만 제대로 작동하지만 group.leave() 여기에서 충돌하고 테스트에서 인쇄 라인 중 하나 또는 두 개가 print("getRekos processing over, handler is called") 이후에 인쇄되고 있음을 확인했습니다. 즉, group.leave()이 for 루프를 완전히 완료하기 전에 호출되거나 일부 반복이 그룹 외부로 "누출"됩니다. 무엇이 이것을 일으킬 수 있습니까? 나는 getExpandedURL에 대한 코드를 게시 한 및 getExpandedURLsFromText 다음 결과 핸들러가 호출 여러 번에 여러 일치 (matches.count > 1)이있는 경우 내 사용자 지정 확장 resolveWithCompletionHandler은 발송 그룹 로직 휴식을, 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 (...) '에 대한 두 번째 호출을 배치 할 수 있습니다. 그러나 'match in matches'루프가 트리거되지 않아야하므로 귀하의 경우에는 안됩니다. – shallowThought

+0

고마워, 그리고 그 블록 (예 :'matches.count == 0 {')이 실행되지 않는다면, 나는 그럴 것이라고 생각한다. – GarySabo

+0

또한'handler'가 전혀 호출되지 않을 때 따라서 그룹은 결코 알리지 않습니다. – Sulthan

답변

1

내부에 사용되는 쪽으로.

논리를 정리하십시오.

성공적으로 완료되면 정확히 완료 핸들러가 호출되고 각 오류마다 정확히 한 번만 호출되어야합니다. guard 문의 여러 곳에서 완성 처리자에게 전화가 없습니다.

+0

당신은 무엇을 권합니까? 'getRekos'에서 무엇을하는지와 비슷하게'getExpandedURL'에서 Dispatch Groups를 사용하고 있습니까? – GarySabo

+0

@GarySabo 그건 아마도 가장 쉬운 방법 중 하나입니다. – Sulthan

+0

'getExpandedURL'의 Dispatch Groups를 사용하여 내 질문을 업데이트했습니다 ... 이것은 원래의 질문을 해결합니다. 더 이상 충돌하지 않고 모든 링크가 반복 처리되었을 때만 알려주지 만'getExpandedURL'의 완료 핸들러는 여전히 호출 중입니다. 여러 번 ... 어떻게 처리기를 한 번만 호출 할 수 있도록'.enter' 및'leave' 호출을 구조화 할 수 있는지 생각해보십시오. – GarySabo