Производительность NSURLSession - Возможные условия гонки или заблокированные потоки?

Я столкнулся с небольшой проблемой производительности моего приложения для iOS, я впервые работаю с NSURLSession и NSURLRequest, и хотя я старался изо всех сил информировать себя, я врезался в стену, пытаясь отладить проблему производительности, с которой я сталкиваюсь.

Итак, вот что я получил: у меня есть приложение для iOS 9, написанное на Swift 2, я общаюсь с сервером NodeJS/Express через запросы Get, Post и Put Http, используя NSURLRequest и NSURLMutableRequest. Я отправляю запросы на выборку группы объектов (вместе не более 12000 байт), однако запросы занимают значительное количество времени (иногда до минуты). Я добавил протоколирование на сервер nodeJs и вижу, что обработка запросов занимает не более 30 миллисекунд.

Примечание: я не уверен, что это уместно, но я использую одноэлементный "вспомогательный" класс для выполнения всех моих запросов API и анализа результатов (сохранение токенов аутентификации, анализ объектов JSON и сохранение их в Core Data, сохранение пользовательских настроек к NSUserDefaults и т. д.), я использую синглтон, чтобы я мог получить к нему статический доступ, и я анализирую все данные, не сохраняя ничего в свойствах синглтона, кроме URL-адреса сервера и NSURLSession.

Вот как выглядит мой код.

//On initialization of the helper class
private let session = NSURLSession.sharedSession() 

func getAllObjects() {
    let route = "api/someRoute"
    let request = getRequest(route)
    request.timeoutInterval = httpTimeout
    session.dataTaskWithRequest(request, completionHandler: ResultingObjects).resume()
}

Метод getRequest возвращает отформатированный NSMutableURLRequest, показанный здесь:

func getRequest(route: String) -> NSMutableURLRequest {
    let request = NSMutableURLRequest()
    request.URL = NSURL(string: "\(serverUrl)/\(route)")!
    request.HTTPMethod = "GET"
    request.addValue("Bearer \(self.AuthenticationToken()!)", forHTTPHeaderField: "Authorization")

    return request
}

Обработчик завершения проанализирует возвращенные объекты и уведомит основной поток с полученными проанализированными объектами следующим образом:

private func ResultingObjects(data: NSData?, response: NSURLResponse?, error: NSError?) {
    if let d = data {

        if !isAuthorized(d){
            return
        }
        do {
            if let JSON = try NSJSONSerialization.JSONObjectWithData(d, options: []) as? NSDictionary {

                if let message = JSON["message"] as? String {
                    if message == "Empty result" {
                        //- Return notification to be handled in main thread
                        notifyMainThread(NoObjectsFetched, payload: nil)
                        return
                    }
                }

                if let objcts = JSON["SomeObjects"] as? NSArray {
                    if let SomeObjects = parseResultingObjects(objcts) {
                        //- Return notification to be handled in main thread
                        notifyMainThread(ObjectsFetched, payload: ["payload": SomeObjects])
                    }
                    return
                }
            }
        }
        catch {
            print("Error getting resulting objects")
        }
    }
    else if let e = error {
        print("\(e), could not process GET request")
    }
}

Я также попытался проанализировать полученные объекты в главном потоке, но, похоже, это не имеет значения.

Если вам интересно, вот как я отправляю данные в основной поток:

private func notifyMainThread(notification: String, payload: AnyObject?) {
    dispatch_async(dispatch_get_main_queue(), {
        if let p = payload {
            NSNotificationCenter.defaultCenter().postNotificationName(notification, object: nil,
                userInfo: p as! [String: [MYMODEL]])
        }
        else {
            NSNotificationCenter.defaultCenter().postNotificationName(notification, object: nil)
        }
    });
}

Что я узнал: ничего не имеет смысла! Я попытался отладить это, но я не могу точно определить, в чем проблема. Когда отладчик нажимает на мой метод "getAllObjects", может пройти несколько хороших секунд (до 45 секунд), прежде чем сервер зарегистрирует, что он получил и обработал запрос (который обычно занимает около 30 миллисекунд). Насколько я могу судить, это происходит для всех типов запросов. Кроме того, после того как приложение вернет данные (очень быстро), потребуется много времени (около 4 секунд) для его анализа и только около 11 КБ.

Я также пытался изменить политику кеширования запросов, если приложение проверяло правильность кэшированных записей на сервере, я использовал ReloadIgnoringLocalAndRemoteCachedData, который тоже не работал.

Теперь это звучит как утечка памяти. Если я приостановлю приложение в любой момент (после нескольких минут его использования), я могу увидеть соответствующее количество потоков. Я, честно говоря, не слишком знаком с IOS, поэтому я не уверен, принадлежат ли эти потоки симулятору или все они принадлежат приложению, приложение передает потоковое видео (без проблем с задержкой) с использованием класса AVPlayer, и я считаю, что многие из этих тем связаны с этим, однако я не уверен, что это нормально, вот скриншот того, что я имею в виду (обратите внимание на полосу прокрутки T_T) Снимок экрана

Может ли быть так, что у меня утечка памяти или некоторые потоки зомби, значительно снижающие производительность моего приложения? Единственная действительно заметная задержка происходит только для HTTP-запросов, что довольно странно, никакая другая часть моего пользовательского интерфейса не задерживается, и ни одна другая функция в моем приложении не страдает от проблем с производительностью (даже потокового видео с URL).

Как лучше всего описать проблемы с производительностью, чтобы точно определить источник проблемы?

ОБНОВЛЕНИЕ 1: Благодаря предложениям Scriptable мне удалось решить проблему с многопоточностью (вызванную тем, что несколько AVPlayers делают свое дело). Выполнение запросов, однако, не решается.

Стоит отметить, что сервер физически находится в той же стране, откуда я делаю запросы, когда при выполнении запросов из браузера или из командной строки запросы почти мгновенные.

Кроме того, когда я случайным образом приостанавливаю приложение (ожидая, пока произойдет запрос), я вижу "mach_msg_trap" в некоторых потоках, я не знаком с этим, но считаю, что это может быть условием гонки? или тупик?

0 ответов

Другие вопросы по тегам