Ios Swift NSURLSERSESSION:同时运行多个请求时出现问题

Ios Swift NSURLSERSESSION:同时运行多个请求时出现问题,ios,swift,nsurlsession,nsjsonserialization,Ios,Swift,Nsurlsession,Nsjsonserialization,我目前正在构建一个天气应用程序,其中我有一个容器ViewController管理多个子ViewController,其主视图位于容器ViewController的UIScrollView中 每个子VC都有一个location属性,该属性是保存所有天气信息的类。此类具有以下函数,用于从OpenWeatherMaps API加载数据: func refresh(completionHandler: () -> ()) { let session = NSURLSession.share

我目前正在构建一个天气应用程序,其中我有一个容器
ViewController
管理多个子
ViewController
,其主视图位于容器
ViewController
UIScrollView

每个子VC都有一个location属性,该属性是保存所有天气信息的类。此类具有以下函数,用于从OpenWeatherMaps API加载数据:

func refresh(completionHandler: () -> ()) {
    let session = NSURLSession.sharedSession()
    session.dataTaskWithURL(self._apiUrl) { (responseData: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
        if let data = responseData {
            do {
                let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)
                // parse json
                completionHandler()
            } catch let err as NSError {
                print(err.debugDescription)
            }
        }
    }.resume()
}
然后,子VC有一个函数调用另一个函数,completionHandler在主线程上运行(因为我正在更新用户界面):

最后,在ContainerVC中,我在我所有的LocationVCs上调用
refreshWeatherData
函数

override func viewDidLoad() {
    super.viewDidLoad()
    let zurich = WALocation(name: "Zürich", id: "7287650", country: "CH", longitude: 8.53071)
    let shanghai = WALocation(name: "Shanghai", id: "1796236", country: "CN", longitude: 121.45)
    let boston = WALocation(name: "Boston", id: "4183849", country: "US", longitude: -83.78)
    let vancouver = WALocation(name: "Vancouver", id: "6173331", country: "CA", longitude: -123.11)

    addLocationControllerForLocation(shanghai)
    locationControllers[0].refreshWeatherData()
    addLocationControllerForLocation(boston)
    locationControllers[1].refreshWeatherData()
    addLocationControllerForLocation(zurich)
    locationControllers[2].refreshWeatherData()
    addLocationControllerForLocation(vancouver)
    locationControllers[3].refreshWeatherData()
}
现在我遇到的问题是,有时候(并非总是),一两次或三次,JSONSerialization抛出一个错误(在位置的刷新函数中):
Error Domain=NSCocoaErrorDomain code=3840“无值。”UserInfo``{NSDebugDescription=No value.}
当我在ContainerVC中执行以下操作时:

override func viewDidLoad() {
    super.viewDidLoad()
    let zurich = WALocation(name: "Zürich", id: "7287650", country: "CH", longitude: 8.53071)
    let shanghai = WALocation(name: "Shanghai", id: "1796236", country: "CN", longitude: 121.45)
    let boston = WALocation(name: "Boston", id: "4183849", country: "US", longitude: -83.78)
    let vancouver = WALocation(name: "Vancouver", id: "6173331", country: "CA", longitude: -123.11)

    addLocationControllerForLocation(shanghai)
    locationControllers[0].refreshWeatherData()
    sleep(1)
    addLocationControllerForLocation(boston)
    locationControllers[1].refreshWeatherData()
    sleep(1)
    addLocationControllerForLocation(zurich)
    locationControllers[2].refreshWeatherData()
    sleep(1)
    addLocationControllerForLocation(vancouver)
    locationControllers[3].refreshWeatherData()
}
NSJSONSerialization从未失败过。这使我认为它只有在多个请求异步运行时才会失败


任何帮助都将不胜感激。

这听起来像是线程问题。您是否尝试过将刷新调用(或刷新本身)封装在GCD调用中?e、 g:

func refresh(completionHandler: () -> ()) {
    dispatch_async(dispatch_get_main_queue()){
        let session = NSURLSession.sharedSession()
        session.dataTaskWithURL(self._apiUrl) { (responseData: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
            if let data = responseData {
                do {
                    let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)
                    // parse json
                    completionHandler()
                } catch let err as NSError {
                    print(err.debugDescription)
                }
            }
            }.resume()
    }
}
如果需要,您可以使用
dispatch\u sync

无论哪种方式,都要确保
completionHandler
在正确的线程上调用它应该调用的任何内容。如果是UI更新,那么它就是主线程

编辑 作为我下面文章的继续,这里是更新的更新,以返回completionHandler中的数据

func refresh(completionHandler: (AnyObject) -> ()) {  //Anyobject being your JsonData or whatever so could be NSString, String, etc...
    let session = NSURLSession.sharedSession()
    session.dataTaskWithURL(self._apiUrl) { (responseData: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
        if let data = responseData {
            do {
                let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)
                // parse json
                completionHandler(json)
            } catch let err as NSError {
                print(err.debugDescription)
            }
        }
    }.resume()
}

这听起来像是线程问题。您是否尝试过将刷新调用(或刷新本身)封装在GCD调用中?e、 g:

func refresh(completionHandler: () -> ()) {
    dispatch_async(dispatch_get_main_queue()){
        let session = NSURLSession.sharedSession()
        session.dataTaskWithURL(self._apiUrl) { (responseData: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
            if let data = responseData {
                do {
                    let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)
                    // parse json
                    completionHandler()
                } catch let err as NSError {
                    print(err.debugDescription)
                }
            }
            }.resume()
    }
}
如果需要,您可以使用
dispatch\u sync

无论哪种方式,都要确保
completionHandler
在正确的线程上调用它应该调用的任何内容。如果是UI更新,那么它就是主线程

编辑 作为我下面文章的继续,这里是更新的更新,以返回completionHandler中的数据

func refresh(completionHandler: (AnyObject) -> ()) {  //Anyobject being your JsonData or whatever so could be NSString, String, etc...
    let session = NSURLSession.sharedSession()
    session.dataTaskWithURL(self._apiUrl) { (responseData: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
        if let data = responseData {
            do {
                let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)
                // parse json
                completionHandler(json)
            } catch let err as NSError {
                print(err.debugDescription)
            }
        }
    }.resume()
}

你在哪里(实际上什么时候)存储天气数据?我在解析天气数据时将其存储,我将注释放在刷新函数的do块中。你在哪里(实际上什么时候)存储天气数据?我在解析天气数据时将其存储,我将注释放在刷新函数的do块中。我已经尝试过这一点,但它没有帮助。我让它工作的唯一方法是刷新位置的completionHandler块中的下一个位置,然后…看起来与
self.location
调用有关。相反,通过完成处理程序传回值不是更好吗?我将在我的回复帖子中给出一个例子。使用它可以确保返回的内容就是发送给完成处理程序的内容。。。所以不必担心线程问题。我测试了你的代码,但没有帮助。无论如何,我都希望将json保存在weather对象中(因为我在解析时正在更新该对象的属性)。在调用另一个API(使用星球大战API进行测试)时,json序列化从未失败。可能是因为我有一个免费的帐户(在openweathermaps上),所以我不能经常调用API。但是谢谢你的努力!既然您提到了它,可能API的其余部分只允许每分钟x个请求以避免DDoS攻击等等……我已经尝试过了,但它没有帮助。我让它工作的唯一方法是刷新位置的completionHandler块中的下一个位置,然后…看起来与
self.location
调用有关。相反,通过完成处理程序传回值不是更好吗?我将在我的回复帖子中给出一个例子。使用它可以确保返回的内容就是发送给完成处理程序的内容。。。所以不必担心线程问题。我测试了你的代码,但没有帮助。无论如何,我都希望将json保存在weather对象中(因为我在解析时正在更新该对象的属性)。在调用另一个API(使用星球大战API进行测试)时,json序列化从未失败。可能是因为我有一个免费的帐户(在openweathermaps上),所以我不能经常调用API。但是谢谢你的努力!既然您提到了它,可能API的其余部分只允许每分钟x个请求,以避免DDoS攻击等。。。。