API线程问题iOS

时间:2017-11-12 14:31:07

标签: ios multithreading

因此,我在运行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参数为空。

例如,如果我在print(character.homeWorld)函数运行后调用getPlanet,我会收到一个空字符串。

我能解决的是解决这个问题的好方法。

1 个答案:

答案 0 :(得分:1)

getPlanet正在执行异步调用。 result实例不会保留接收的数据。而不是使用getPlanet中的完成块并在接收数据时调用此完成块。像这样的东西。请阅读closures

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 ?? ""
                        self.getPlanet(uri: (charJSON?["homeworld"] as? String)?.description ?? "", completion:
                            { (result:String) in
                                character.homeWorld = result
                                DispatchQueue.main.async {
                                    self.delegate?.didGetStarWarsCharacter(characterData:character)
                                }
                        }
                        )
                        //The homeward part of the response is another URL and as such requires another API Call to get anything meaningful

                    }catch{
                        print("Error downloading character information. Empty or incomplete character returned")
                    }
                }
            }
        }
        task.resume()
    }
    private func getPlanet(uri:String, completion:@escaping (_ response:String)->Void){
        if uri == ""{
            completion(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"
                    }
                    completion(result)
                }
            }
        }// end of task, result is lost due to multithreading
        task.resume()
    }
}