如何从同步 NSURL 转换为异步 NSURL 连接



我正在尝试更新我用ASOC编写的旧Mac OS程序(主要是Applescript,但也有一些用于Web服务访问之类的ObjC对象)。我使用了同步连接:

NSData *resultsData = [NSURLConnection sendSynchronousRequest: req returningResponse: &response error: &err];

服务器凭据嵌入在 URL 中。这对我来说效果很好,因为在获取数据时程序确实无法继续执行任何操作。但是,对服务器身份验证方法的更改迫使需要对此应用程序进行更改。我已经尝试了所有常用的解决方法,但仍然不适用于此服务。

所以看起来我需要更改为异步调用:

[[NSURLConnection alloc] initWithRequest:request
                                delegate:self 
                        startImmediately:YES];

我使用适当的委托方法,最重要的是:

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

虽然我很想使用某种形式的延迟循环来检查数据何时完成加载(基本上使其再次同步),但我还没有找到一种实际上不会阻止连接的方法。

我能够使用 NSTimer 等待数据,然后再继续:

set theJobListTimer to current application's NSTimer's scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(0.05, me, "jobListTimerFired:", "", true)
    on jobListTimerFired_(theTimer)
      (jobListData as list) & count of jobListData
    if count of jobListData ≠ 0  then
        log "jobListTimerFired_ done"
        tell theTimer to invalidate()
        setUpJobList(jobListData)
    end if
end jobListTimerFired_

但这很笨拙,当我处于模式对话框中时不起作用:

    set buttonReturned to current application's NSApp's runModalForWindow_(collectionWindow)

(我在对话框中有一个下拉菜单,需要使用 Web 服务调用的结果进行更新)。现在,委托方法被阻止,直到模式被关闭。

难道没有简单的方法可以使用异步方法模拟同步调用吗?

尝试使用信号量,我将代码更改为:

- (void) startConnection:(int)reqType :(NSMutableURLRequest *)request {
requestType = [NSNumber numberWithInt:reqType];
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    // This could be any block that is run asynchronously
    void (^myBlock)(void) = ^(void) {
        self.connection = [[NSURLConnection alloc] initWithRequest:request
                                                          delegate:self 
                                                  startImmediately:YES];
    myBlock();
if (self.connection) {
        // create an object to hold the received data
        self.receivedData = [NSMutableData data];
        NSLog(@"connection started %@", requestType);
    }
   dispatch_time_t timeOut = dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC);
    dispatch_semaphore_wait(semaphore, timeOut);
    dispatch_release(semaphore);
    semaphore = NULL;
}

然后在连接处理程序中:

- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@"connectionDidFinishLoading %@", requestType);
NSString *returnData = [[NSString alloc] initWithData:receivedData
                                             encoding:NSUTF8StringEncoding] ;
//      NSLog(@"connectionDidFinishLoading %@", returnData);
[self handleData:requestType :returnData];
[self terminate];
if(semaphore) {
    dispatch_semaphore_signal(semaphore);
}
}

但是,connectionDidFinishLoading 处理程序(以及 didReceiveResponse 和 didReceiveData 处理程序)直到 10 秒调度超时后才会被调用。我在这里错过了什么?

您可以使用

dispatch_semaphore_wait将任何异步 API 再次转换为同步 API。

下面是一个示例:

__block BOOL accessGranted = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// This could be any block that is run asynchronously
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
    accessGranted = granted;
    if(semaphore) {
        dispatch_semaphore_signal(semaphore);
    }
});
// This will block until the semaphore has been signaled
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
semaphore = NULL;
return accessGranted;

在这里找到答案:

iOS, NSURLConnection: 在不同的线程上委托回调?

我知道连接在不同的线程上运行,并尝试了各种其他 while 循环来等待它完成。但这真的是魔术线:

    while(!self->finished]){
    //This line below is the magic!
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

最新更新