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攻击等。。。。