这是一个很难搜索的人。 我发现了一个类似的问题iOS 5 Wait for delegate to finish before populating a table?,但接受的答案是“刷新表格视图”,这对我没有帮助。我发现的其他结果往往是在c#中。
我有一个从iPhone流向Wowza服务器的应用程序。当用户点击记录时,我生成一个唯一的设备ID,然后将其发送到服务器上的PHP脚本,该脚本返回带有配置设置的JSON文档(包括rtmp转储链接)。
问题是,委托方法是异步的,但我需要在- (IBAction)recordButtonPressed
方法的下一行代码之前获取配置设置,因为该代码设置了配置文件设置,然后基于记录在那些设置上。
我已经意识到我可以像{I}当前那样在NSURLConnection
中创建-recordButtonPressed
,然后在委托方法connectionDidFinishLoading
中继续设置代码(或者只是封装设置和方法从那里调用它)但是这牺牲了功能的连贯设计而且很糟糕。
我是否可以向委托人发送一些简单的waitUntilDelegateIsFinished:(BOOL)nonAsyncFlag
标志,以便我可以通过顺序操作从网络中提取数据?
答案 0 :(得分:1)
我已经意识到我可以在-recordButtonPressed中创建NSURLConnection,就像我当前一样,然后在委托方法connectionDidFinishLoading中继续设置代码(或者只是封装设置和方法从那里调用它)但是这会牺牲连贯的设计功能很糟糕。
您已经分析并了解了这种情况,并且您已经完美地描述了其可能的解决方案。我只是不同意你的结论。这种事情一直在发生:
- (void) doPart1 {
// do something here that will eventually cause part2 to be called
}
- (void) doPart2 {
}
你可以通过调用来玩各种游戏,使其更加优雅和通用,但我的建议是,不要与框架作斗争,因为你所描述的正是异步的本质。 (并且不在主线程上使用同步请求,因为这会阻塞主线程,这是禁止的。)
事实上,在一个事件驱动的框架中,“等到”的概念就是诅咒。
答案 1 :(得分:0)
为什么不使用synchronous request?
答案 2 :(得分:0)
将一个包含完成块作为参数的辅助方法包装异步NSURLConnection请求:
-(void) asyncDoSomething:(void(^)(id result)completionHandler ;
此方法应在NSURLConnectionDelegate
中实施。有关详细信息,请参阅下面的示例实现和注释。
在其他地方,在你的行动方法中:
设置完成处理程序。该块将在主线程上进一步调度,然后执行任何适当的更新表数据,除非结果是错误,在这种情况下,您应该显示警报。
- (IBAction) recordButtonPressed
{
[someController asyncConnectionRequst:^(id result){
if (![result isKindOfClass:[NSError class]]) {
dispatch_async(dispatch_get_main_queue(), ^{
// We are on the main thread!
someController.tableData = result;
});
}
}];
}
方法asyncConnectionRequst:
的实现可以如下工作:取块并将其保存在ivar中。如果合适,请使用正确的参数调用它。但是,将块作为ivars或属性会增加无意中引入循环引用的风险。
但是,还有一种更好的方法:将立即将包装器块分派给挂起的串行调度队列 - 该队列作为ivar保存。由于队列被挂起,它们不会执行任何块。只有在队列恢复之后,才会执行该块。您可以在connectionDidFinish:
和connectionDidFailWithError:
(见下文)中恢复队列:
在你的NSURLConnectionDelegate:
-(void) asyncConnectionRequst:(void(^)(id result)completionHandler
{
// Setup and start the connection:
self.connection = ...
if (!self.connection) {
NSError* error = [[NSError alloc] initWithDomain:@"Me"
code:-1234
userInfo:@{NSLocalizedDescriptionKey: @"Could not create NSURLConnection"}];
completionHandler(error);
});
return;
}
dispatch_suspend(self.handlerQueue); // a serial dispatch queue, now suspended
dispatch_async(self.handlerQueue, ^{
completionHandler(self.result);
});
[self.connection start];
}
然后在NSURLConnectionDelegate中,调度一个处理程序并恢复 处理程序队列:
- (void) connectionDidFinishLoading:(NSURLConnection*)connection {
self.result = self.responseData;
dispatch_resume(self.handlerQueue);
dispatch_release(_handlerQueue), _handlerQueue = NULL;
}
同样发生错误时:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.result = error;
dispatch_resume(self.handlerQueue);
dispatch_release(_handlerQueue), _handlerQueue = NULL;
}
还有更好的方法,但是它涉及一些更基本的辅助类,它们处理异步体系结构,这些体系结构在一天结束时使异步代码看起来像是同步的:
-(void) doFourTasksInAChainWith:(id)input
{
// This runs completely asynchronous!
self.promise = [self asyncWith:input]
.then(^(id result1){return [self auth:result1]);}, nil)
.then(^(id result2){return [self fetch:result2];}, nil)
.then(^(id result3){return [self parse:result3];}, nil)
.then(^(id result){ self.tableView.data = result; return nil;}, ^id(NSError* error){ ... })
// later eventually, self.promise.get should contain the final result
}