【问题标题】:URLSession.shared.dataTask freezes the UIURLSession.shared.dataTask 冻结 UI
【发布时间】:2019-09-16 14:21:22
【问题描述】:

我正在为 iOS 开发一个 Swift 应用程序,但我提出的请求有问题。

此请求每 3 到 5 秒发出一次,正文是一个 5k 行 JSON(总共约 130k 个字符),用于刷新 UITableView。问题是每次在此特定请求上使用.dataTask 时,应用程序都会冻结,然后在发出请求后正常运行。

“冻结”是指我在滚动长 UITableView 时检测到它。即使是一个小的滚动列表也会冻结。

我首先怀疑UITableView 的更新,但我添加了一个小“hack”,在滚动时禁用UITableView 的更新,如下所示:

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        print("started scrolling")
        self.currentlyScrolling = true
    }

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        print("stopped scrolling")
        self.currentlyScrolling = false
    }

它不起作用。

我的问题是:我认为 dataTask 不在主线程中运行。但我不确定。我怎样才能检查它?另外,如何确定 dataTask 是异步的?

dataTask 代码如下所示:

let task = URLSession.shared.dataTask(with: req) { data, response, err in
        guard let data = data, err == nil else {
            // error
            return
        }

        if let resp = try? JSONSerialization.jsonObject(with: data) {
            // success
        }
    }

    task.resume()

不过,请注意,它仅在 JSONSerialization 之前冻结,而不是之后冻结,所以我想这不是导致冻结的原因。

【问题讨论】:

    标签: ios swift uitableview urlsession nsurlsessiondatatask


    【解决方案1】:

    URLSession.shared.dataTask 在后台线程中运行,您可以移动 JSONSerialization,如果您正在更新 UI,则将其全部移动到主线程中

    let task = URLSession.shared.dataTask(with: req) { data, response, err in {
    
        // HERE YOU ARE IN BACKGROUND THREAD, see print result in debug area
    
        print("You are on \(Thread.isMainThread ? "MAIN" : "BACKGROUND") thread.")
    
        guard let data = data, err == nil else { return }
    
        if let resp = try? JSONSerialization.jsonObject(with: data) {
            DispatchQueue.main.async {
                print("Now moved to \(Thread.isMainThread ? "MAIN" : "BACKGROUND") thread")
    
                // success, ANY UI UPDATES MUST BE MADE HERE
            }
        }
    }
    

    你的 JSON 解析应该在后台线程上,所以这个解析操作不会阻塞主线程上的其他东西,比如 UI。

    以下是使用在主线程上调用的完成块的示例

    func myApiTask(with request: URLRequest, completion:@escaping (_ data: Any?) -> Void) {
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
    
            var resp: Any?
            defer {
                DispatchQueue.main.async{ completion(resp) }
            }
    
            if data == nil && error == nil {
                resp = try? JSONSerialization.jsonObject(with: data!)
            } else {
                // error handling
            }
    
        }
    }
    

    用法

    myApiTask(with: req) { data in
        // you are on main thread
    }
    

    【讨论】:

    • 感谢您的回答。看起来并不能解决问题。但是,我发现使用Timer 重复调用足够聪明,可以在用户滚动时自动停止。虽然这并不能解决这里的问题,但现在已经足够了。无论如何,谢谢!
    • 我隐约记得共享会话使用主队列进行完成回调,以与 NSURLConnection 兼容,因此您的代码可能已经在主队列上运行。尝试使用不同(或 nil)的 NSOperationQueue 创建您自己的会话,或者将上述方法的全部内容显式分派到低优先级分派队列中。
    猜你喜欢
    • 2022-01-25
    • 2015-01-16
    • 1970-01-01
    • 2015-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-30
    相关资源
    最近更新 更多