URLSession.shared.dataTask冻结UI

时间:2019-04-27 07:26:19

标签: ios swift uitableview urlsession nsurlsessiondatatask

我正在为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之前冻结,而不是之后冻结,因此我想这不是导致冻结的原因。

1 个答案:

答案 0 :(得分: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
}