URLSession.shared.dataTask останавливает пользовательский интерфейс - PullRequest
1 голос
/ 27 апреля 2019

Я делаю приложение Swift для iOS, и у меня возникла проблема с запросом, который я делаю.

Этот запрос выполняется каждые 3-5 секунд, а тело представляет собой JSON размером 5 тыс. Строк (всего ~ 130 тыс. Символов), который обновляет UITableView. Проблема заключается в том, что каждый раз, когда .dataTask используется по этому конкретному запросу, приложение останавливается, а затем выполняется нормально после выполнения запроса.

Под "зависанием" я подразумеваю, что обнаружил это, когда прокручиваю свой длинный UITableView. Даже небольшой список прокрутки зависает.

Сначала я заподозрил обновление UITableView, но добавил небольшой хак, который отключает обновление 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 Ответ

1 голос
/ 27 апреля 2019

URLSession.shared.dataTask выполняется в фоновом потоке, вы можете переместить JSONSerialization, и если вы обновляете перемещение пользовательского интерфейса, все в основном потоке

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 должен выполняться в фоновом потоке,поэтому эта операция синтаксического анализа не будет блокировать другие объекты, такие как пользовательский интерфейс, в главном потоке.

Ниже приведен пример, в котором используется блок завершения, вызываемый в главном потоке

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
}
...