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");

Получавам всички числа, отпечатани от главното изпълнение, но completionHandler никога не се изпълнява. Опитах и ​​това с помощта на делегат, без успех.

Благодаря предварително.

РЕДАКТИРАНЕ Както беше предложено, промених функцията си на следното:

-(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
Редактирах публикацията с вашето предложение, но дори и с новата функция не получавам реакция от completionHandler - person ylc; 25.03.2014
comment
@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
Относно вашия въпрос за тестване на единица, това вероятно е съвсем различен проблем, при което тестовете на единица се изпълняват на главната опашка и ако ще извиквате асинхронни методи, трябва да ги направите синхронни в името на теста (чрез използване на семафор или група за изпращане). Използвате ли семафор или dispatch_group, за да сте сигурни, че основната нишка е блокирана, докато мрежовата заявка приключи? (BTW, докато тази техника работи с NSURLSession, ако блокът за завършване изпрати нещо до главната опашка, можете да блокирате, така че трябва да внимавате.) - person Rob; 14.05.2014
comment
@Rob Не, не използвам семафор или dispatch_group. Този проблем звучи като нова тема (която ясно сте споменали в първото си изречение по-горе). Ще пусна нова тема, в която да опиша какво се случва. - 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