Обработчик завершения NSURLSessionDataTask dataTaskWithURL не вызывается

В последнее время я изучаю Objective C и решил попробовать подключения.

С NSURLConnection все было отлично, пока я не обнаружил, что он устарел, и не попытался работать с NSURLSession.

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

Вот используемый код:

NSURL * url = [NSURL URLWithString:@"http://api.openweathermap.org/data/2.5/weather?q=London,uk"];

NSLog(@"2");
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSLog(@"3");

NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject
                                                             delegate: nil
                                                        delegateQueue: [NSOperationQueue mainQueue]];

NSLog(@"4");
NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithURL:url
                                                completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                    dispatch_sync(dispatch_get_main_queue(), ^{
                                                        NSLog(@"11");
                                                        if(error == nil)
                                                        {
                                                            NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
                                                            NSLog(@"Data = %@",text);
                                                        }
                                                        NSLog(@"22");
                                                    });

                                                }];

NSLog(@"5");
[dataTask resume];
NSLog(@"6");

Я получаю все числа, напечатанные из основного выполнения, но завершениеHandler никогда не выполняется. Я также пробовал это с помощью делегата, но безуспешно.

Заранее спасибо.

EDIT Как и было предложено, я изменил свою функцию на следующую:

-(void) doGET{
    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration     defaultSessionConfiguration];
    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject];
    NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithURL:[self url]
                                                    completionHandler:^(NSData *data,    NSURLResponse *response, NSError *error) {
                                                            NSLog(@"11");
                                                            if(error == nil)
                                                            {
                                                                NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
                                                                NSLog(@"Data = %@",text);
                                                            }
                                                            NSLog(@"22");

                                                    }];
    [dataTask resume];

}

Мой менеджер завершения все еще не запускается. Я также пытался использовать sharedSession вместо передачи конфигурации, но тоже не повезло.

Спасибо за помощь


person ylc    schedule 24.03.2014    source источник
comment
Вы когда-нибудь заставляли этот код работать? Мой код почти идентичен (я думаю, мы посещали одни и те же веб-сайты), и я тоже не могу вызвать блок кода завершения. Однако я запускаю свой код в качестве модульного теста с симулятором iOS 7.1, и мне интересно, имеет ли это какое-либо влияние. Я также возился с NSURLSession, и ничего из этого не помогло. Я заметил, что вы пометили вопрос как ответ, но все еще не ясно, каков был ответ?   -  person Jane Wayne    schedule 14.05.2014


Ответы (2)


Если вы собираетесь использовать представление блока завершения задачи данных, а не указывать delegate из nil, например:

NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject
                                                             delegate: nil
                                                        delegateQueue: [NSOperationQueue mainQueue]];

Вместо этого вы должны создать экземпляр своего сеанса, используя метод, который вообще не принимает delegate:

NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject];

Или, в качестве альтернативы, учитывая, что вы вообще не настраиваете свою конфигурацию, вы можете вообще пропустить создание экземпляра NSURLSessionConfiguration и использовать общий NSURLSession:

NSURLSession *defaultSession = [NSURLSession sharedSession];

Но в итоге вы не должны использовать представление sessionWithConfiguration:delegate:queue:, если только вы не собираетесь реализовывать делегаты, и в этом случае вы не будете использовать представление метода NSURLSessionDataTask с параметром completionHandler.

Кроме того, убедитесь, что ваше устройство/симулятор работает под управлением iOS 7.0 или более поздней версии.

person Rob    schedule 24.03.2014
comment
Я пытался сделать это с помощью различных подходов: используя собственный делегат, предложение, которое вы дали, и то, что я разместил выше. Проблема в том, что я не получаю действия NSLog из обработчика завершения (или делегата, когда я его использовал). Мой код представляет собой смесь всех подходов, которые я использовал, поэтому, вероятно, он не в лучшем виде, но я не могу заставить ни делегата, ни обработчика завершения ничего не печатать. Я не уверен, почему, поскольку все, что я читал, указывает на то, что так и должно быть. Знаете ли вы, есть ли какая-либо другая причина, по которой он не «выполнит» обработчик завершения? Заранее спасибо - person ylc; 25.03.2014
comment
Я отредактировал сообщение с вашим предложением, но даже с новой функцией я не получаю никакой реакции от завершенияHandler - person ylc; 25.03.2014
comment
@user3214816 user3214816 Вы используете это на iOS 7? Если вы работаете на iOS 6.1, вы не получите исключение (что меня удивило), а скорее defaultSession будет nil, как и dataTask. Кроме этого сценария, я не знаю, почему это не сработает, потому что ваш код отлично работает на моем устройстве iOS 7.x и симуляторе iOS 7.x. Я бы предложил зарегистрировать defaultSession и dataTask и посмотреть, возвращаете ли вы действительные объекты. - person Rob; 25.03.2014
comment
Я запускал его в приложении командной строки, так как просто тестировал его, прежде чем делать приложение для дыр. Спасибо, с реальным приложением работает отлично! - person ylc; 08.04.2014
comment
@Rob При поиске в Интернете есть множество примеров публикации данных, которые следуют точно такому же коду, который представил OP (например, используйте представление блока завершения задачи данных). Итак, если мы последуем вашему совету (без делегата, следовательно, без блока завершения), как мы должны обрабатывать HTTP-ответ? Если мы просто не публикуем и не заботимся об ответе, я не вижу, как этот совет может быть полезен (если нет другого способа подключиться к обратному вызову после получения ответа HTTP). - person Jane Wayne; 14.05.2014
comment
@JaneWayne Я думаю, ты меня неправильно понял. Я не предлагал вам использовать представление без делегата и без блока завершения. Я просто говорю, что если вы используете представление блока завершения, вы не должны указывать конфигурацию с делегатом nil, а скорее указывать конфигурацию без параметра делегата. Очевидно, вам нужно использовать представление блока завершения или представление на основе делегата. Дело в том, что вы не можете использовать шаблон блока завершения с конфигурацией, которая говорит, что есть делегат, но это nil. - person Rob; 14.05.2014
comment
@Rob Это немного проясняет. Но можете ли вы уточнить язык, вы не можете использовать шаблон блока завершения...? Вы имеете в виду, что вам НЕ следует использовать... вместо того, чтобы использовать... (поскольку это плохая практика программирования)? Потому что я указал делегата nil и у меня есть блок завершения. Когда я запускаю этот код из своего AppDelegate (didFinishWithLaunchingOptions), вызывается код блока завершения и запускаются точки отладки (симулятор iOS 7.1). Странно только то, что код блока завершения не срабатывает при модульном тестировании. Есть мысли по этому поводу? - person Jane Wayne; 14.05.2014
comment
@JaneWayne В своих экспериментах я столкнулся с поведением, описанным в OP, когда использование шаблона блока завершения с делегатом nil не работало, тогда как когда я указывал конфигурацию с использованием представления без делегата, вызывались блоки завершения. Возможно, вы нашли сценарий, в котором он действительно работает (и в документации сказано, что должен), но я определенно смог воспроизвести проблему OP, когда указание делегата nil предотвратило вызов блока завершения, тогда как указание делегата не позволяло выполнение блока завершения. - person Rob; 14.05.2014
comment
Что касается вашего вопроса модульного тестирования, то это, вероятно, совсем другая проблема, при которой модульные тесты запускаются в основной очереди, и если вы собираетесь вызывать асинхронные методы, вы должны сделать их синхронными ради теста (используя семафор или диспетчерская группа). Используете ли вы semaphore или dispatch_group, чтобы убедиться, что основной поток заблокирован до завершения сетевого запроса? (Кстати, хотя этот метод работает с NSURLSession, если блок завершения отправляет что-либо в основную очередь, вы можете зайти в тупик, поэтому вам нужно быть осторожным.) - person Rob; 14.05.2014
comment
@Rob Нет, я не использую семафор или диспетчерскую_группу. Эта проблема звучит как новая тема (о которой вы четко упомянули в своем первом предложении выше). Я опубликую новую тему, описывающую, что происходит. - person Jane Wayne; 14.05.2014
comment
Для будущих читателей модульное тестирование NSURLSession рассматривается здесь: stackoverflow.com/questions/23657486/ - person Rob; 14.05.2014
comment
У вас должна быть сильная ссылка на NSURLSession, если вы создаете ее и не используете [NSURLSession sharedSession] - person Raman; 22.11.2017

Проверьте расположение вашего кода.

Хотя в OP не указано, где был расположен код, после долгих поисков и множества итераций я обнаружил, что размещение примера кода в -viewDidLoad никогда не приводило к выполнению обработчика завершения. Перемещение кода в

- (void)viewWillAppear:(BOOL)animated { }

решена проблема с никогда не завершающейся задачей. (Хотя я до сих пор не понимаю, почему.)

person David    schedule 19.02.2016