【发布时间】:2017-11-13 01:15:21
【问题描述】:
所以我在运行 API 调用时遇到问题,该调用的响应包含另一个 API 调用。
这是第一个函数:
class APICaller{
weak var delegate:APIDelegate?
func getCharacter(x:Int){
let character = CharacterModel()
let url = URL(string: "https://swapi.co/api/people/\(x)/")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil{
print("Error downloading character information. Empty character returned.")
} else {
if let content = data {
do{
let charJSON = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any]
character.name = (charJSON?["name"] as? String)?.description ?? ""
character.height = Int((charJSON?["height"] as? String)?.description ?? "0") ?? 0
character.mass = Int((charJSON?["mass"] as? String)?.description ?? "0") ?? 0
character.hairColor = (charJSON?["hair_color"] as? String)?.description ?? ""
character.skinColor = (charJSON?["skin_color"] as? String)?.description ?? ""
character.eyeColor = (charJSON?["eye_color"] as? String)?.description ?? ""
character.birthYear = (charJSON?["birth_year"] as? String)?.description ?? ""
character.gender = (charJSON?["gender"] as? String)?.description ?? ""
character.homeWorld = self.getPlanet(uri: (charJSON?["homeworld"] as? String)?.description ?? "")
//The homeward part of the response is another URL and as such requires another API Call to get anything meaningful
DispatchQueue.main.async {
self.delegate?.didGetStarWarsCharacter(characterData:character)
}
}catch{
print("Error downloading character information. Empty or incomplete character returned")
}
}
}
}
task.resume()
}
private func getPlanet(uri:String)->String{
if uri == ""{
return uri // return empty string if the original call doesn't get anything.
}
var result = ""
let url = URL(string:uri)
let task = URLSession.shared.dataTask(with: url!){(data,response,error)->Void in
if error != nil{
result = "No Planet Found"
}else{
if let planet = data{
do{
let planetJSON = try JSONSerialization.jsonObject(with: planet, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String:Any]
//print(planetJSON?["name"] as? String ?? "No Planet")
result = (planetJSON?["name"] as? String)?.description ?? "No Planet Found"
}catch{
result = "No Planet Found"
}
}
}
}// end of task, result is lost due to multithreading
task.resume()
return result
}
}
所以我知道为getPlanet 运行任务发生在另一个线程上,并且此方法在任务完成运行之前返回。因此,当委托获取 CharacterModel 时,其 homeWorld 参数为空。
例如,如果我在 getPlanet 函数运行后调用 print(character.homeWorld),我会收到一个空字符串。
我想不出这个问题的一个很好的解决方案。
【问题讨论】:
-
将
escaping closure添加到您的getPlanet(uri:String)方法以将结果返回给调用者。当您从服务器成功接收数据时调用该闭包。 stackoverflow.com/a/45976392/5912335 -
所以你是说在任务中我应该有一个注解为
@escaping的闭包,它将结果发送回类 API Caller 的对象? -
没错。并从方法定义中删除您的
getPlanet方法的返回值 (-> String)。这不是必需的。 -
好的。所以删除返回,可能设置一个快速的 String 实例变量,并在从调用中获取 String 后,将其发送到转义闭包中的该实例变量。之后剩下的就是如何确保在调用委托的
didGetStarWarsCharacter之前将该值添加到角色的 homeWorld 参数中。 -
这很简单。当调用
getPlanet方法时,您将声明与之关联的闭包。因此,当您在服务响应后调用escaping closure时,将执行关联的闭包并传递结果字符串。您可以使用它来设置homeWorld属性。这个答案是您了解如何在服务调用后使用闭包返回数据所需的全部内容:stackoverflow.com/a/39504347/5912335
标签: ios multithreading