ios - swift NSURLSession : trouble when running multiple requests at the same time

标签 ios swift nsurlsession nsjsonserialization

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

每个子 VC 都有一个位置属性,它是保存所有天气信息的类。此类具有以下用于从 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 在主线程上运行(因为我正在更新用户界面):

func refreshWeatherData() {
    location.refresh {
        dispatch_async(dispatch_get_main_queue(), {
            print("\(self.location.name)")
            self.forecastCollectionView.reloadData()
        })
    }
}

最后,我从 ContainerVC 调用了我所有 LocationVC 上的 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()
}

现在我遇到的问题是有时(不总是),一两次或三次,JSONSerialisation 抛出错误(在位置的刷新函数中): 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()
}

NSJSONSerialisation 永远不会失败。这让我觉得只有当多个请求异步运行时它才会失败。

如有任何帮助,我们将不胜感激。

最佳答案

这听起来像是一个线程问题。您是否尝试过将刷新调用(或刷新本身)包含在 GCD 调用中?例如:

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_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()
}

关于ios - swift NSURLSession : trouble when running multiple requests at the same time,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36456385/

相关文章:

swift - 按照 Xcode 的 Apple 'Food Tracker' 教程 - 无法获取更改标签文本的按钮

c - 在 Swift 中使用 CFNotificationCallback,或者在 Swift 中使用 @convention(c) block

ios - 如何通过 AFNetworking 和 NSUrlSession 调用 SOAP Web 服务

ios - NSURLSession 正确获取数据,但在 Task.resume() 之后不返回任何内容

ios - 对齐 UIView 和 CAShapeLayer 时遇到问题

iphone - 使用内部自定义图像创建像素完美的标准 UIBarButtonItem

javascript - JS区分iPhone上的点击和滚动

ios - 当我快速滚动表格时,未调用 cellForRowAtIndexpath 方法

ios - 为什么我的 dateFormatter 只能在 24 小时制设备设置下工作?

iOS 网络 - HTTP 连接 & 在后台运行