1

这是一个很难搜索的。我发现了一个类似的问题,iOS 5 Wait for delegate to finish before populating a table? ,但接受的答案是“刷新表格视图”,这对我没有帮助。我发现的其他结果往往是在 c# 中。

我有一个从 iPhone 流式传输到 Wowza 服务器的应用程序。当用户点击记录时,我生成一个唯一的设备 ID,然后将其发送到服务器上的 PHP 脚本,该脚本返回一个带有配置设置的 JSON 文档(包括 rtmp 转储链接)。

问题是,委托方法是异步的,但我需要在我的方法中的下一行代码之前获取配置设置- (IBAction)recordButtonPressed,因为该代码是设置配置文件设置的内容,然后根据这些设置进行记录。

我已经意识到我可以像现在一样进入NSURLConnection-recordButtonPressed然后在委托方法中继续设置代码或者connectionDidFinishLoading只是封装设置和从那里调用它的方法),但这牺牲了功能的连贯设计,这很糟糕。

是否没有一些简单waitUntilDelegateIsFinished:(BOOL)nonAsyncFlag的标志可以发送给委托人,以便我可以进行从网络中提取数据的顺序操作?

4

3 回答 3

1

我已经意识到我可以像现在一样在 -recordButtonPressed 中制作 NSURLConnection,然后在委托方法 connectionDidFinishLoading 中继续设置代码(或者只是封装设置和从那里调用它的方法),但这会牺牲功能的连贯设计,并且糟透了。

您已经分析并了解了情况,并且完美地描述了可能的解决方案。我只是不同意你的结论。这种事情一直在发生:

- (void) doPart1 {
    // do something here that will eventually cause part2 to be called
}

- (void) doPart2 {
}

您可以通过调用玩各种游戏,以使其更加优雅和通用,但我的建议是,不要与框架抗争,因为您所描述的正是异步的本质。(并且不要主线程上使用同步请求,因为这会阻塞主线程,这是一个禁忌。)

实际上,在事件驱动的框架中,“等到”这个概念本身就是一种诅咒。

于 2013-05-17T16:17:29.467 回答
0

为什么不使用同步请求

于 2013-05-17T15:50:13.267 回答
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:and中恢复队列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        
}
于 2013-05-19T19:45:46.797 回答