2011-12-08 115 views
13

如何让我的程序在转到下一行代码之前等待异步NSURLConnection完成?如何让程序在继续执行下一个命令之前等待异步NSURLConnection完成?

SetDelegate *sjd= [SetDelegate alloc]; 
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:post delegate:sjd]; 
[connection start]; 

我这是怎么开始的连接,我办理的委托接收到的数据,但我想等待连接到主要出发前结束,因为这是一个for循环,它具有为每个运行元素在我的数据库中。

我需要将电话数据库中的数据放到远程数据库,并且数据成功放入电话数据库中的数据后将被删除。我正在浏览手机数据库中的每个元素,并开始一个连接,这就是为什么我看不到循环中可以完成的下一个任务。我是一个初学者,当谈到Objective-C编程,所以我不知道这是正确的方式还是不这样做

使调用同步不是一个选项,因为它阻止了程序,我有应该显示的进度条。

+1

任何理由,你为什么不能简单地做,从委托,而不是正确的,在循环“下一步”的东西? :) –

+0

是否避免以并列方式启动许多请求?如果是这样,你的委托方法可以负责从服务器获取下一个结果。有很少的情况下,你真的想阻止HTTP请求,特别是如果你正在做很多事情。 –

+0

我遇到了与OP相同的情况,我有一个应用程序在后台线程中大量使用同步调用,现在想要支持使用异步使用的自定义身份验证(使用NSURLConnection的委托方法)。相反,重写应用程序,我可以“包装”异步调用,基本上使它们同步,因为它们已经发生在后台线程上,一切都很好。 – Aaron

回答

19

你的问题有点奇怪。你不可能限制这个问题。你不能让一行代码“等待”一个进程完成而没有阻塞某些进程,在这种情况下,无论循环运行什么线程。

如果你想使用同步调用,它不会不会阻止你的应用程序,它只会阻止它被执行的线程。在你的例子中,你有一个持续获取远程数据的循环,你希望你的UI能够反映出来,直到完成。但你不希望你的用户界面被阻止。这意味着,你的循环线程已经必须在后台线程,所以你可以随意在没有阻塞你的UI线程的循环中进行同步调用。如果循环在UI线程上,你需要改变它来做你想做的事情。

您也可以使用异步连接执行此操作。在这种情况下,您的操作可能实际完成更快的银行,您可以同时处理多个请求。如果你这样做,你的循环可以保持在UI线程上,你只需要跟踪所有的连接,这样当它们完成后,你可以将这个状态传递给相关的控制器。您需要iVars来跟踪加载状态,并在加载完成后使用协议或NSNotification进行通信。

编辑:同步调用后台线程,将实施例

如果您希望循环完全只完成时,所有的请求都完成并不能阻止这里的UI线程是一个简单的例子:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    // post an NSNotification that loading has started 
    for (x = 0; x < numberOfRequests; x++) { 
     // create the NSURLRequest for this loop iteration 
     NSURLResponse *response = nil; 
     NSError *error = nil; 
     NSData *data = [NSURLConnection sendSynchronousRequest:request 
              returningResponse:&response 
                 error:&error]; 
     // do something with the data, response, error, etc 
    } 
    // post an NSNotification that loading is finished 
}); 

无论对象需要显示加载状态,都应该观察并处理您在此处发布的通知。循环将通过后台线程同步完成所有请求,并且您的UI线程将被解除阻塞并进行响应。这不是唯一的方法,事实上,我会自己使用异步连接,但这是一个简单的例子,说明如何获得你想要的。

+0

@pillblast,所有应用程序执行你要做的事情(下载一些东西并让UI响应并反映状态之前,期间和之后)通过保持UI线程畅通并且异步执行加载操作。你可以通过使用异步NSURLConnection或上面的示例来做到这一点,后者在后台线程上执行同步请求。主线程(UI)上没有阻塞线程的同步操作。 – XJones

+0

谢谢你的回答。对不起,在赏金结束之前我不能选择它作为答案,但它与圣诞节假期相交。我按照你的建议使用后台线程完成了它,它的工作原理与我想要的完全一样。我只是在动作发生时禁用Tab控制器,一切都很好。 – Pillblast

+0

在联网完成之前,您应该使用下面的建议,而不是阻止UI。如果您使用像杰瑞建议的通知,则您的用户界面不会被阻止,只有在网络连接完成后才会更新。 – anders

-1

NSURLConnection已经是异步的。只需实现委托方法。如果您想更新的MainThread(例如进度条)用户界面,您可以在didReceiveData. 这样做或this

+0

我的问题是关于使它几乎同步,所以我不明白你的答案 – Pillblast

3

看,如果你真的想要等待,为什么要使用异步调用呢?请使用同步呼叫代替:

NSURLResponse* response = nil; 
NSData* data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:nil] 

此方法将阻止执行的线程,因此您应该确保要执行此操作!我提到它会阻止吗? :)

+0

那么,这就是为什么我不想使用同步调用,因为它会阻止应用程序。这不是一个选项。 – Pillblast

+0

所以你想要发生一系列的下载,有一个进度指示器,并允许用户做其他事情,更正?一个可能的途径是采取这整个行动,并将其纳入NSOperation。您是否应该在进度条上显示每个单独下载的进度或下载集?你下载了多少项目?它们有多大? –

+0

他们不是很大,但他们可能是数百人。问题是,即使在NSOperation中,我也会遇到同样的问题,我现在在for循环中使用异步NSURlConnection。 – Pillblast

1

你说你想等待异步调用完成,所以我假设你正在调用你在一个单独的线程发布的代码。

我建议看看新的sendAsynchronourRequest方法。我有posted up an example of how you can wrap this up in a class,当连接完成/超时/失败时,它会通知它的委托人。我只是把你引用到这篇文章中,因为它听起来像是在尝试实现与我当时所做的非常相似的事情,而这个DownloadWrapper类对我来说完美无瑕。这是iOS5中的新功能,请注意。

5

如果您只是想知道它何时完成并且不关心任何数据,只需使用NSNotificationCenter发布通知并让您的视图订阅它。

代表 - 完成后发布通知

-(void) connectionDidFinishLoading:(NSURLConnection*)connection { 
    [[NSNotificationCenter defaultCenter] postNotificationName:@"NSURLConnectionDidFinish" object:nil]; 
} 

查看 - 添加观察者和观察

-(void) viewDidLoad { 

[[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(yourCleanupMethod) 
               name:@"NSURLConnectionDidFinish" 
               object:nil]; 
} 

-(void) yourCleanupMethod { 
    // finish up 

    [[NSNotificationCenter defaultCenter] removeObserver:self]; 
} 

现在,当运行一些代码,如果你需要通过一个简单的对象追溯到数据,你可以尝试加载你的通知中的对象参数,如下所示:

[[NSNotificationCenter defaultCenter] postNotificationName:@"NSURLConnectionDidFinish" object:yourDataObject]; 

然后改变你的观点和清理签名是这样的:

-(void) viewDidLoad { 

// Notice the addition to yourCleanupMethod 
[[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(yourCleanupMethod:) 
               name:@"NSURLConnectionDidFinish" 
               object:nil]; 
} 

-(void) yourCleanupMethod:(NSNotification *)notif { 
    // finish up 
    id yourDataObject = [notif object]; 

    [[NSNotificationCenter defaultCenter] removeObserver:self]; 
} 

现在我发现自己需要的东西比这多一点,所以我结束了创建一个单身处理我所有的请求。由于NSURLConnectionDelegate中的所有代理方法都为您提供了特定连接的NSURLConnection实例,因此您可以将简单的数据对象存储在字典中,并且每次都可以通过连接查找它。从那里我有一个方法签名,采取和对象和选择器中,我与连接关联,所以在所有事情已经结束后,我可以通过在该对象上执行选择器将该可变数据对象传递给请求者。

我不会在这里包含所有的代码,但也许这会帮助你思考什么是可能的。我发现我在编写Web服务调用时遇到了很多代码,因此将所有东西封装在一个单例中给了我一个很好的获取数据的简洁方法。希望这可以帮助!

+1

当事件发生时,通知对于创建“回叫”非常有帮助。我会建议这种方法 – anders

1

This金块帮助我! 我正在使用同步NSURL很好,直到我决定我需要SSL为我的客户端和我的服务器之间的连接。我采用了key pinning的方法,该方法将设备上的证书与服务器上的证书进行比较(请参阅上面的链接),为了使其工作,我需要将代码添加到NSURL方法中,从我的研究中可以与NSURL不同步。 直到我发现了这个可笑的简单解决方案,它为我工作:

NSString *connectionRunLoopMode = @"connectionRunLoopMode"; 
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:urlRequest delegate:urlConnectionDelegate startImmediately:NO]; 
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; 
[connection unscheduleFromRunLoop:currentRunLoop forMode:NSDefaultRunLoopMode]; 
[connection scheduleInRunLoop:currentRunLoop forMode:connectionRunLoopMode]; 
[connection start]; 
while ([currentRunLoop runMode:connectionRunLoopMode beforeDate:[NSDate distantFuture]]); 
+0

while line引发sigabrt错误,当我运行这个 – Aaron

+0

@Aaron阅读以下内容:http://stackoverflow.com/questions/8072135/how-to-track-down-cause-of-sigabrt – ConfusedDeer

+1

我最终将这段代码缩短为'NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:urlConnectionDelegate];我的同步调用(在后台线程上)变成支持身份验证委托的异步调用,它允许我将同步调用(在后台线程上)转换为支持身份验证委托的异步调用方法,同时仍然是同步的,以免重大更改我的应用程序。 – Aaron

相关问题