Метод делегата NSURLConnection не вызывается - PullRequest
0 голосов
/ 10 июля 2020

Я новичок в Objective- C, и я работал над старым кодом, пытаясь динамически проверять, следует ли игнорировать ошибки SSL-сертификата или нет. Я уже установил делегат NSURLConnection и его методы:

@interface Downloader : NSObject <NSURLConnectionDelegate>

-(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:( NSURLAuthenticationChallenge *)challenge;

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;

-(void)connectionDidFinishLoading:(NSURLConnection *)connection;

Реализация:

-(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
    if([self.ignoreCertificateErrors isEqualToString:@"false"]){
        if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
            [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
    }
    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    CFRunLoopStop(CFRunLoopGetCurrent());
    _downloadError = error;
}

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [_downloadData appendData: data];
}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSError * error;
    [_downloadData writeToFile:_downloadDest options:NSDataWritingAtomic error:&error];
}

Проблема, с которой я столкнулся, заключается в том, что -(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:( NSURLAuthenticationChallenge *)challenge не вызывается перед каждым download, поэтому свойство не проверяется, и загрузка происходит независимо от значения переменной.

Кто-нибудь знает, что может заставить NSURLConnection игнорировать свои собственные методы делегата?

(Кроме того, загрузки происходит асинхронно с использованием NSRunLoop currentRunLoop

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

Это отрывок из кода, который выполняет асинхронную загрузку:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t downloadQueue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 );
dispatch_async(downloadQueue, ^{
        NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
                                                                      delegate:self
                                                              startImmediately:NO];
        [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [connection start];
        [ [ NSRunLoop currentRunLoop ] run ];
        dispatch_semaphore_signal(semaphore);
        [connection release];
    });
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

Ответы [ 2 ]

1 голос
/ 10 июля 2020

Если делегат никогда не вызывается, очень вероятно, что ваш ответственный объект не существует во время выполнения. В этом случае правильная реализация делегата будет проверять, существует ли объект для вызова, имеет ли ожидаемый тип данных и протокол, а также проверяет, существует ли метод селектора. Когда все это не является вашей причиной проблем, это может быть из-за отсутствия или неправильного параметра / объекта, который был передан селектору объектов делегата или никогда не был вызван вовремя.

Часто делегат просто не устанавливается после выделения (из class с использованием делегата) или указывая на неправильный тип класса или просто NULL, и поэтому никогда не обрабатывались.

решение: проверьте, может ли ваш id<DelegateProtocol> указывать на существующий объект во время выполнения и настроен так, чтобы следовать правильный протокол.

напоминание: реализация Classname <ProtocolA, ProtocolB> помогает вам кодировать все необходимые методы делегирования и публикует определения методов в интерфейсе для вас. Если нет - вы должны увидеть любое предупреждение Xcode об этом.

подсказка: в некоторых случаях, даже если в документации указано, что есть значения по умолчанию, вам нужно установить делегата. А NSURLConnection устарел.

0 голосов
/ 10 июля 2020

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

Как бы то ни было, правильная техника запуска NSURLConnection в фоновом потоке является сложной. Например, вы звоните CFRunLoopStop в случае неудачи, но не в случае успеха. И использование run категорически не рекомендуется. Как в документации сказано:

Если вы хотите, чтобы выполнение l oop завершилось, вы не должны использовать этот метод [run]. Вместо этого используйте один из других методов запуска, а также проверьте другие произвольные условия самостоятельно, в al oop. Простым примером может быть:

BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

где shouldKeepRunning установлен на NO где-то еще в программе.

Но прежде чем беспокоиться об этом, мы должны остановиться и спросите, почему мы вообще используем этот фоновый поток. Намного проще просто запланировать NSURLConnection на основном прогоне l oop. NSURLConnection является асинхронным (если вы избегаете использования sendSynchronousRequest), поэтому нет необходимости отправлять начало этих запросов в фоновую очередь. NSURLConnection не будет блокировать основной поток. (Излишне говорить, что если вы выполняете какую-либо сложную обработку в методах делегата NSURLConnection, тогда вы можете отправить это в какую-то очередь GCD, но само соединение можно просто добавить к основному runl oop без инцидентов. .)

Если вы действительно хотите использовать фоновый поток для NSURLConnection, не используйте семафоры. И используйте runMode:beforeDate: вместо run. Или, учитывая, что NSURLConnection в любом случае устарел, используйте NSURLSession, который может использовать фоновый поток для своего делегата без runl oop глупости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...