自动搜索时快速取消 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的主要内容,如果未能解决你的问题,请参考以下文章

vs code 打开文件时,取消文件目录的自动定位跟踪

当键盘从自己的文本字段切换到另一个文本字段时,搜索栏右键“取消”被禁用

如何实现将网页内容自动推送给百度搜索引擎

UISearchBar 在取消时消失

AFNetworking 和自动建议

如何快速取消已经关注的微信公众号(包括订阅号、服务号)?