Swift 3逃脱关闭更改不会粘连

时间:2017-12-04 22:30:22

标签: swift multithreading closures

我正在尝试使用OMDb API编写权力的游戏情节纲要。我从服务器上获得的信息很好,但我很难将数据发送到我的UITableView。

这是调用闭包的函数和定义它的函数:

func getAllForSeason(season: Int) {
    let uri = "\(baseURL)\(urlId)&season=\(season)"
    var episodeList = [EpisodeModel]()
    let url = URL(string: uri)
    let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
      if error != nil {
        print("Error retrieving information. No season found")
      } else {
        if let content = data {
          do {
            let seasonJSON = try JSONSerialization.jsonObject(with: content, options: .mutableContainers) as? [String: Any]
            let episodesFound = seasonJSON?["Episodes"] as! [[String: String]]
            for episodeJSON in episodesFound {
              let number = Int(episodeJSON["Episode"] ?? "0") ?? 0
              var episodeToAdd = EpisodeModel()
              self.getTemporaryEpisode(season: season, number: number, completion: {(result:EpisodeModel) in
                episodeToAdd = result
                print(episodeToAdd.season)
              })
              print(episodeToAdd.season)
              episodeList.append(episodeToAdd)
            }
            DispatchQueue.main.async {
              self.delegate?.didGetSeason(episodes: episodeList)
            }
          } catch {
            print ("Error retrieving information. No seasons found")
          }
        }
      }
    }
    task.resume()
  }


private func getTemporaryEpisode(season: Int, number: Int, completion: @escaping(_ response:EpisodeModel)->Void) {
    let episode = EpisodeModel()
    let uri = "\(baseURL)\(urlId)&season=\(season)&episode=\(number)"
    let url = URL(string: uri)
    let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
      if error != nil {
        print("Error downloading episode information. Empty Episode returned")
      } else {
        if let content = data {
          do {
            let episodeJSON = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any]
            episode.title = (episodeJSON?["Title"] as? String)?.description ?? ""
            let dateString = (episodeJSON?["Released"] as? String)?.description ?? ""
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy MMM dd"
            episode.releaseDate = dateFormatter.date(from: dateString) ?? Date()
            episode.rating = (episodeJSON?["Rated"] as? String)?.description ?? ""
            episode.season = Int(episodeJSON?["Season"] as? String ?? "") ?? 0
            episode.number = Int(episodeJSON?["Episode"] as? String ?? "") ?? 0
            let runtimeString = (episodeJSON?["Runtime"] as? String)?.description ?? "0 min"
            let spaceIndex = runtimeString.index(of: " ") ?? runtimeString.endIndex
            let minuteString = runtimeString[runtimeString.startIndex..<spaceIndex]
            episode.minutes = Int(minuteString) ?? 0
            episode.director = (episodeJSON?["Director"] as? String)?.description ?? ""
            episode.writer = (episodeJSON?["Writer"] as? String)?.description ?? ""
            let actorsString = (episodeJSON?["Actors"] as? String)?.description ?? ""
            episode.actors = actorsString.components(separatedBy: ", ")
            episode.plot = (episodeJSON?["Plot"] as? String)?.description ?? ""
          } catch {
            print("Error downloading episode information. Empty or incomplete episode returned")
          }
        }
      }
      completion(episode)
    }
    task.resume()
  }

我使用完全相同的方法对SW API(星球大战)做过类似的工作,然后就可以了。

用于测试的印刷语句应打印使用getTemporaryEpisode

获得的剧集的季节编号

但是,我发现当第一个print语句正确打印时(从闭包内),闭包之外的第二个打印0打印0(默认我设置以确保值被初始化),这意味着{{1} }不再包含结果。

例如:Winter Is Coming将在机箱内打印1,但在其中打印0。

它首先输出0,所以我确定它是一个线程问题,并且在第二个print语句被调用时没有发生完成。我只是不确定如何解决它。

编辑:我确实意识到我愚蠢地将我的调用放在循环内的主线程中,但修复它并没有解决这个问题。

1 个答案:

答案 0 :(得分:0)

您的函数getTemporaryEpisode是异步的,因此您通过一组空集来通知委托。

然后,您重新分配episodeToAdd变量以引用正确设置的剧集,但这对episodeList的内容没有任何影响。您可能希望等到填充episodeList,直到您检索到正确的内容为止。您可以使用DispatchGroup

但是,如果您只是使用DispatchGroup,则剧集不需要以预期的顺序返回,因为它们将分别在同一时间独立地请求。最简单的解决方案是将它们收集在一个数组中,然后按照剧集编号对数组进行排序,然后通知您的代表。