1

我正在开发从 Web 服务器获取数据的 ios 应用程序。我希望其他一切都等待,直到调用并完成此类的处理程序之一。我知道使用调度/线程是可能的,但我就是不知道怎么做。

-(void)callWebService:(NSString*)URL:(NSString*)SOAP{
     NSURL *url = [NSURL URLWithString:URL];
     NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];

     [req setHTTPMethod:@"POST"];
     [req setHTTPBody:[SOAP dataUsingEncoding:NSUTF8StringEncoding]];


     NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:req delegate:self];
     if(con){
         [con start];
     }
}

并且在这个方法的最后继续这个类之外的代码。但我想等到这个处理程序被调用(并完成):

-(void)connection:(NSURLConnection *)c didReceiveData:(NSData *)data{
    NSString *res = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"%@",res);
    Ukol_Parser *parser = [Ukol_Parser alloc];
    [parser parseUkol:res];
}

因为这里的解析器将数据放入 sqlite db 并且正在读取此类数据之外的数据。但是“外部”代码的执行速度比我得到响应和调用处理程序的速度要快......

4

5 回答 5

2

如果您想要“等待其他所有内容”,那么听起来您真正想要做的是同步请求。

查看[NSURLConnection sendSynchronousRequest:returningResponse:error:]

但是,请确保在后台线程上执行此操作,因为如果您在主线程上执行此操作,您的 UI 将阻塞,并且您的应用程序看起来对用户触摸或其他任何内容都没有响应。

于 2012-11-10T19:16:57.320 回答
1

我很紧张您从后台队列中接受了有关的答案,因为从实际角度来看,这与您的基于 - 的实现sendSynchronousRequest没有什么不同。didReceiveData具体来说,如果您确实从后台队列执行同步请求,它不会让“其他一切都等待”。但是,如果您忽略了从后台队列执行此同步请求(即,如果您从主线程执行此请求),您最终会得到一个可怕的用户体验(应用程序被冻结并且用户想知道应用程序是否已崩溃),并且更糟糕的是,如果花费太长时间,您的应用程序可能会被 iOS“看门狗进程”杀死。

完全尊重各种答案,在后台队列上发送同步请求与现有NSURLConnectionDataDelegate的基于 - 的方法没有区别。您真正需要做的是接受应用程序的其余部分不会冻结这一事实,因此只需更新 UI 以让用户知道发生了什么,即 (a) 提供一些应用程序没有死机的视觉提示;(b) 阻止用户与您现有的 UI 进行交互。

例如,在发出网络请求之前,添加一个视图来覆盖/调暗 UI 的其余部分,防止用户与现有 UI 交互,并添加一个微调器,让用户知道应用程序正忙于做某事。因此,为 a 定义一个类属性,UIView它将使 UI 的其余部分变暗:

@property (nonatomic, strong) UIView *dimView;

然后,在网络请求之前,更新 UI:

// add a view that dims the rest of the UI, so the user has some visual cue that they can't use the UI yet
// by covering the whole UI, you're effectively locking the user out of using the app
// until the network request is done

self.dimView = [[UIView alloc] initWithFrame:self.view.bounds];
self.dimView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
self.dimView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.dimView];

// add a spinner that shows the user that something is really happening

UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicatorView.center = self.dimView.center;
indicatorView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[indicatorView startAnimating];
[self.dimView addSubview:indicatorView];

[self callWebService:URL withSOAP:SOAP];

然后,在connectionDidFinishLoading方法中(或者如果您sendSynchronousRequest从后台队列中使用,则在代码的后半部分中调度到该后台队列),在完成数据解析后,您希望:

[self.dimView removeFromSuperview];
self.dimView = nil;

// now do what ever to need do to update your UI, e.g.:
//
// [self.tableView reloadData] 

但关键是您绝对不想从主队列发出同步网络请求。您应该 (a) 异步执行网络请求(在后台队列上同步或按照您最初实现的方式),以便 iOS 看门狗进程不会杀死您的应用程序;(b) 让用户知道应用程序正在发出一些网络请求并且没有冻结它们(例如 a UIActivityIndicatorView;和 (c) 当请求完成时,删除这些“应用程序正在发出网络请求”的 UI 元素并刷新现在您的网络请求已完成,其余的 UI。

最后,在测试您的应用程序时,请确保您在真实的网络环境中对其进行测试。我建议您安装硬件 IO 工具(可在 Xcode 菜单中的“打开开发者工具”-“更多开发者工具”下找到)并查看网络链接调节器。这让您可以在 iOS 模拟器上模拟真实世界的网络情况(例如,糟糕的 3G 或 Edge 网络条件)。当我们在具有理想网络连接的典型开发环境中测试我们的应用程序时,我们会陷入一种错误的性能感。“在野外”的设备会遭受各种网络性能下降的情况,最好在类似的、次优的网络情况下测试您的应用程序。

于 2013-08-25T04:36:22.217 回答
0

一种疯狂的解决方案,但这实际上有效https://gist.github.com/62940:D

于 2012-11-10T19:27:39.117 回答
0

使用同步调用。但它会改变你的类的设计,因为同步调用会阻塞并使应用程序挂起

于 2012-11-10T19:46:10.830 回答
0

从您的 didReceiveData: 方法发布通知,并让您的其他类观察该通知(或者您可以使用委托设置,如果很容易从其他类获取对此类的引用,那么您可以将其他类设置为这个)。在通知的选择器方法中,开始执行其余代码。

于 2012-11-10T20:49:36.803 回答