自动搜索时快速取消 URLSession
Posted
技术标签:
【中文标题】自动搜索时快速取消 URLSession【英文标题】:URLSession Cancellation in swift while auto search 【发布时间】:2017-02-08 04:21:11 【问题描述】:我的 moto 是自动搜索,我一直在尝试使用 URLSession,当我尝试缓慢搜索时,请求按预期处理(当没有文本时,响应为空,我的意思是 placesarray)但是当我试图清除文本或快速点击任何搜索文本,然后将先前的请求响应附加到位置数组中。我尝试取消先前的请求但我没有得到结果(即未附加先前的响应)
func autoSearch(text:String)
let urlRequest = URLRequest(url: self.getQueryFormedBingURL()!)
let session = URLSession.shared
session.getTasksWithCompletionHandler
(dataTasks, uploadTasks, downloadTasks) -> Void in
// self.cancelTasksByUrl(tasks: dataTasks as [URLSessionTask])
self.cancelTasksByUrl(tasks: uploadTasks as [URLSessionTask])
self.cancelTasksByUrl(tasks: downloadTasks as [URLSessionTask])
let task = session.dataTask(with: urlRequest, completionHandler: (data, response, error) -> Void in
print("response \(response)")
if let data = data
let json = try? JSONSerialization.jsonObject(with: data, options: [])
if let response = response as? HTTPURLResponse , 200...299 ~= response.statusCode
if let jsonDic = json as? NSDictionary
let status = jsonDic.returnsObjectOrNone(forKey: "statusCode") as! Int
if status == 200
if let resourceSetsArr = jsonDic.returnsObjectOrNone(forKey: "resourceSets") as? NSArray
if let placesDict = resourceSetsArr.object(at: 0) as? NSDictionary
if let resourceArr = placesDict.object(forKey: "resources") as? NSArray, resourceArr.count > 0
if let _ = self.placesArray
self.placesArray!.removeAll()
for loopCounter in 0...resourceArr.count - 1
let modalClass:BingAutoCompletePlace = BingAutoCompletePlace(responseDict: resourceArr[loopCounter] as! NSDictionary)
self.placesArray?.append(modalClass)
completion(self.placesArray!)
else //URL Success, where there no places with the given search string
completion([])
else if let response = response as? HTTPURLResponse , 400...499 ~= response.statusCode // When url fails
if let _ = error
print("error=\(error!.localizedDescription)")
completion([])
else
if let _ = error
print("error=\(error!.localizedDescription)")
completion([])
)
task.resume()
//请求取消
private func cancelTasksByUrl(tasks: [URLSessionTask])
for task in tasks
task.cancel()
【问题讨论】:
【参考方案1】:不幸的是,框架不保证任务完成的任何顺序——因为这取决于运行时间。也可能是您处于当前已取消任务的完成处理程序中。
要避免这种情况,您可以执行以下操作:
创建一个私有实例变量来存储最近的任务 像以前一样取消其他所有内容 在完成处理程序中 检查任务是否仍然是最近的任务(如if (task !== self.currentTask) return
)
创建一个本地数组来存储数据
在主线程中更新视图控制器数组 (DispatchQueue.main.async(...)
)
我清理了你的代码(使用guard
语句来最小化嵌套)。也许你也应该
guard
语句返回)
将任务交给completion
调用并再次检查该任务是否仍然是当前任务。这也是将currentTask
重置为nil
的好方法。
根据您的需要采用它 :-)
var currentTask:URLSessionDataTask?
func autoSearch(text:String)
let completion:(_ x:[AnyObject])->() = _ in
let urlRequest = URLRequest(url: self.getQueryFormedBingURL()!)
let session = URLSession.shared
session.getTasksWithCompletionHandler
(dataTasks, uploadTasks, downloadTasks) -> Void in
// self.cancelTasksByUrl(tasks: dataTasks as [URLSessionTask])
self.cancelTasksByUrl(tasks: uploadTasks as [URLSessionTask])
self.cancelTasksByUrl(tasks: downloadTasks as [URLSessionTask])
var task:URLSessionDataTask!
task = session.dataTask(with: urlRequest, completionHandler: (data, response, error) -> Void in
print("response \(response)")
if (task !== self.currentTask)
print("Ignore this task")
return
if let error = error
print("response error \(error)")
guard let data = data else return
let json = try? JSONSerialization.jsonObject(with: data, options: [])
var newPlacesArray = [AnyObject]() // Empty array of whichever type you want
if let response = response as? HTTPURLResponse , 200...299 ~= response.statusCode
guard let jsonDic = json as? NSDictionary else return
let status = jsonDic.returnsObjectOrNone(forKey: "statusCode") as! Int
if status == 200
guard let resourceSetsArr = jsonDic.returnsObjectOrNone(forKey: "resourceSets") as? NSArray else return
guard let placesDict = resourceSetsArr.object(at: 0) as? NSDictionary else return
guard let resourceArr = placesDict.object(forKey: "resources") as? NSArray, resourceArr.count > 0 else
//URL Success, where there no places with the given search string
DispatchQueue.main.async completion(newPlacesArray)
return
for loopCounter in 0...resourceArr.count - 1
let modalClass:BingAutoCompletePlace = BingAutoCompletePlace(responseDict: resourceArr[loopCounter] as! NSDictionary)
newPlacesArray.append(modalClass)
DispatchQueue.main.async completion(newPlacesArray)
else if let response = response as? HTTPURLResponse , 400...499 ~= response.statusCode // When url fails
if let _ = error
print("error=\(error!.localizedDescription)")
DispatchQueue.main.async completion(newPlacesArray)
else
if let _ = error
print("error=\(error!.localizedDescription)")
DispatchQueue.main.async completion(newPlacesArray)
)
self.currentTask = task
task.resume()
【讨论】:
以上是关于自动搜索时快速取消 URLSession的主要内容,如果未能解决你的问题,请参考以下文章