我正在尝试更新我用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]];
}