【问题标题】:NSURLConnection Delegate method not being called未调用 NSURLConnection 委托方法
【发布时间】:2020-07-10 04:18:12
【问题描述】:

我是 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 在每次下载之前都不会被调用,因此不会检查该属性,并且无论变量的值如何,都会进行下载。

有谁知道什么会导致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);

【问题讨论】:

  • 控制台告诉你什么? NSURLConnection 不需要应用程序传输吗?我已经超过 4 年没有使用它了。所以我不记得它是如何工作的。
  • 只有这一个没有被调用的委托方法吗?如果没有人被调用,则表明存在许多问题中的任何一个。如果只有这个被调用,那表明一些不同的东西。
  • ignoreCertificateErrors是什么数据类型?
  • 请分享您如何设置delegate。作为委托的类也应该符合委托协议:@interface MyClass:NSObject&lt;NSURLConnectionDelegate&gt; {...}
  • @Rob 刚刚检查,其他方法也没有被调用

标签: objective-c macos ssl ssl-certificate nsurlconnection


【解决方案1】:

如果从未调用过委托,则很可能您的负责对象在运行时不存在。在这种情况下,适当的委托实现将检查要调用的对象是否存在,是否具有预期的数据类型和协议,以及检查选择器方法是否存在。当所有这些都不是您的问题时,可能是因为向委托对象选择器提供了丢失或错误的参数/对象,或者从未及时调用。

通常在分配(使用委托的类)或指向错误的类类型或只是 NULL 之后未设置委托,这就是为什么从未处理过。

解决方案: 检查您的 id&lt;DelegateProtocol&gt; 是否可以在运行时指向现有对象并配置为遵循正确的协议。

提醒: Classname &lt;ProtocolA, ProtocolB&gt; 的实现可帮助您编写所有需要的委托方法,并为您在接口中发布方法定义。如果没有 - 你应该会看到任何关于它的 Xcode 警告。

提示:在某些情况下,即使文档说有默认值,您也需要设置委托。并且 NSURLConnection 已被弃用。

【讨论】:

  • 首先,感谢您的回答。除了设置NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self startImmediately:NO]; 之外,如何正确设置委托?或者我如何证明该方法设置正确?
  • [connection setDelegate:self]; 并检查 if (connection.delegate != nil) {..}BOOL nice = [connection.delegate conformsToProtocol:@protocol(YourProtocol)];
【解决方案2】:

您说您正在启动一个异步请求,但使用信号量实际上是在使用异步 API 并使其行为同步。除非绝对必要,否则通常应避免使用信号量使异步方法同步。这里似乎不太合适。您正在阻止您调用它的任何线程。可以想象,你也可能陷入僵局。

不管怎样,在后台线程上启动NSURLConnection 的正确技术是复杂的。例如,您在失败时调用CFRunLoopStop,而不是在成功时调用。并且明确不鼓励使用run。正如the documentation 所说:

如果你想终止运行循环,你不应该使用这个 [run] 方法。相反,请使用其他运行方法之一,并在循环中检查您自己的其他任意条件。一个简单的例子是:

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

其中shouldKeepRunning 在程序的其他位置设置为NO

但是在担心这个之前,我们应该停下来问问我们为什么要使用这个后台线程。在主运行循环上安排NSURLConnection 要容易得多。 NSURLConnection 是异步的(只要您避免使用sendSynchronousRequest),因此无需将这些请求的开始分派到后台队列。 NSURLConnection 不会阻塞主线程。 (不用说,如果您在 NSURLConnection 委托方法中进行任何复杂的处理,那么您可能会将其分派到某个 GCD 队列,但连接本身可以毫无意外地添加到主运行循环中。)

如果您真的想为NSURLConnection 使用后台线程,请不要使用信号量。并使用runMode:beforeDate: 而不是run。或者,考虑到NSURLConnection 无论如何都已被弃用,请使用NSURLSession,它可以使用后台线程作为其委托,而不会出现运行循环的愚蠢。

【讨论】:

    【解决方案3】:

    您应该在这里使用performDefaultHandlingForAuthenticationChallenge:,而不是continueWithoutCredentialForAuthenticationChallenge:。否则,您将破坏任何基于钥匙串的身份验证,并且可能会破坏许多其他内容。

    另外,在开始请求之前,您需要在连接上调用setDelegateQueue:。否则,您可能会主动阻塞发送回调的队列。

    最后,您真的不应该忽略证书错误。允许具有已知公钥的特定证书是可以的,但忽略证书错误是一个非常糟糕的主意,即使对于内部构建也是如此,因为这些类型的代码往往会意外地最终出现在生产环境中。 :-)

    【讨论】:

      【解决方案4】:

      您的调度进入队列,然后离开块。一旦你离开块连接被释放并超出范围。尝试在块之前声明您的连接,然后等待然后释放。

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

      【讨论】:

        猜你喜欢
        • 2014-07-03
        • 2011-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-29
        • 1970-01-01
        相关资源
        最近更新 更多