ios 如何让程序在继续下一个命令之前等待异步 NSURLConnection 完成?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8427836/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 15:47:25  来源:igfitidea点击:

How to make program wait for asynchronous NSURLConnection to finish before proceeding with next command?

iosweb-services

提问by Pillblast

How can I make my program wait for an asynchronous NSURLConnection to finish before going to the next line of code?

如何让我的程序在进入下一行代码之前等待异步 NSURLConnection 完成?

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

This is how I start the connection and I handle the data received in the delegate but I want to wait for the connection to end before proceeding mainly because this is in a for loop and it has to run for each element in my database.

这就是我启动连接并处理委托中接收到的数据的方式,但我想在继续之前等待连接结束,主要是因为这是在 for 循环中,它必须为我的数据库中的每个元素运行。

I need to put data from the phones database to a remote database and after the data was successfully put in the data in the phones database is deleted. I am going through each element in the phone's database and start a connection that's why I don't see how the next stuff can be done from the loop. I'm a beginner when it comes to objective-c programming so I'm not sure if this is the right way or not to do it

我需要将手机数据库中的数据放入远程数据库,并在数据成功放入手机数据库后删除数据。我正在浏览手机数据库中的每个元素并启动一个连接,这就是为什么我看不到如何从循环中完成接下来的工作。我是objective-c编程的初学者,所以我不确定这是否是正确的方法

Making the call synchronous is not an option because it blocks the program and i have a progress bar that should show.

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

回答by XJones

Your question is a bit odd. You have impossibly constrained the issue. You cannot have a line of code "wait" for a process to finish w/o it blocking something, in this case whatever thread the loop is running in.

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

You can use a synchronous call if you wanted to, it doesn't block your app, it only blocks the thread it is executed on. In your example, you have a loop that is continually getting remote data and you want your UI to reflect that until it is done. But you don't want your UI blocked. That means, this thread with your loop already MUST be on a background thread so you can feel free to do a synchronous call in the loop w/o blocking your UI thread. If the loop is on the UI thread you need to change this to do what you want.

如果需要,您可以使用同步调用,它不会阻塞您的应用程序,它只会阻塞执行它的线程。在您的示例中,您有一个不断获取远程数据的循环,并且您希望您的 UI 在完成之前反映它。但是您不希望您的 UI 被阻止。这意味着,这个带有循环的线程必须已经在后台线程上,这样你就可以随意在循环中进行同步调用而不会阻塞你的 UI 线程。如果循环在 UI 线程上,您需要更改它以执行您想要的操作。

You could also do this using an asynchronous connection. In that case, your operation may actual complete faster b/c you can have multiple requests in progress at the same time. If you do it that way, your loop can remain on the UI thread and you just need to track all of the connections so that when they are finished you can communicate that status to the relevant controllers. You'll need iVars to track the loading state and use either a protocol or NSNotification to communicate when loading is done.

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

EDIT: ADDED EXAMPLE OF SYNCHRONOUS CALL ON BACKGROUND THREAD

编辑:添加了后台线程同步调用的示例

If you want the loop to finish completely only when all requests are finishes and not block your UI thread here's a simple example:

如果您希望循环仅在所有请求完成时才完全完成并且不阻塞您的 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
});

Whatever objects need to show loading status should observe and handle the notifications you post here. The loop will churn through and make all your requests synchronously on a background thread and your UI thread will be unblocked and responsive. This isn't the only way to do this, and in fact, I would do it using async connections myself, but this is a simple example of how to get what you want.

任何需要显示加载状态的对象都应该观察并处理您在此处发布的通知。该循环将在后台线程上运行并同步发出您的所有请求,并且您的 UI 线程将被解锁和响应。这不是做到这一点的唯一方法,事实上,我自己会使用异步连接来做到这一点,但这是一个如何获得所需内容的简单示例。

回答by jerrylroberts

If you just want to know when it's complete and don't really care about any data, simply use the NSNotificationCenterto post a notification and have your view subscribe to it.

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

Delegate- Post Notification upon completion

委托- 完成后发布通知

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

View- Add observer and run some code when observed

查看- 添加观察者并在观察时运行一些代码

-(void) viewDidLoad {

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

-(void) yourCleanupMethod {
    // finish up

    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Now, if you need to pass a simple object back as data you can try loading up the object parameter in your notification like this:

现在,如果您需要将一个简单的对象作为数据传回,您可以尝试在通知中加载对象参数,如下所示:

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

Then change your view and cleanup signature like this:

然后像这样更改您的视图和清理签名:

-(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];
}

Now I found myself needing something a little more than this so I ended up creating a singleton to handle all of my requests. Since all of your delegate methods in NSURLConnectionDelegategive you and instance of the NSURLConnectionfor the specific connection, you can simply store a mutable data object in a dictionary and look it up each time by the connection. From there I have a method signature that takes and object and selector in that I associate with the connection so after everything has wrapped up, I can pass that mutable data object to the requestor by performing the selector on that object.

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

I won't include all of that code here but maybe that will help get you thinking about what is possible. I found that I had a lot of code tied up in making web service calls so wrapping everything up in a singleton gave me a nice clean way of getting data. Hope this helps!

我不会在这里包含所有这些代码,但也许这会帮助你思考什么是可能的。我发现在进行 Web 服务调用时我有很多代码,所以将所有内容都封装在一个单例中为我提供了一种非常干净的获取数据的方式。希望这可以帮助!

回答by Rob Reuss

If you really want it to wait, why use an asynchronous call at all? Use a synchronous call instead:

如果你真的想让它等待,为什么要使用异步调用呢?改用同步调用:

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

This approach will block the thread it's executed, so you should be sure you want to do it! Did I mention that it will block? :)

这种方法会阻塞它执行的线程,所以你应该确定你想这样做!我有没有提到它会阻塞?:)

回答by ConfusedDeer

Thisgolden nugget helped me! I was using synchronous NSURL just fine until I decided I needed SSL for my connection between my client and my server. I took the approach of key pinningwhich is comparing the cert on the device to the cert on the server (read more on link above) and in order for it to work I needed to add code to the NSURL methods, which from my research you can't do with NSURL synchronous. Until I found this ridiculously simple solution which worked for me:

这个金块帮了我!我一直在使用同步 NSURL,直到我决定我的客户端和服务器之间的连接需要 SSL。我采用了密钥固定的方法,即将设备上的证书与服务器上的证书进行比较(在上面的链接中阅读更多内容),为了使其正常工作,我需要将代码添加到 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]]);

回答by pxlshpr

You say you want to wait for an asynchronouscall to complete, so I'm assuming you're calling the code you posted up in a separate thread.

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

I would recommend having a look at the new sendAsynchronourRequestmethod. I've posted up an example of how you can wrap this up in a classwhich would inform its delegate when the connection has completed / timed out / failed. I'm only referring you to this post because it sounds like you're trying to achieve something very similar to what I was at the time, and this DownloadWrapperclass worked flawlessly for me. It's new in iOS5, mind you.

我建议看看新sendAsynchronourRequest方法。我已经发布了一个示例,说明如何将其包装在一个类中该类会在连接完成/超时/失败时通知其委托。我只是向你推荐这篇文章,因为听起来你正在努力实现与我当时非常相似的东西,而这DownloadWrapper门课对我来说完美无缺。请注意,这是 iOS5 中的新功能。

回答by Alex Moskalev

NSURLConnection is already asynchronous. Simply implement the delegate methods. If you want to update the UI on the MainThread (e.g. a progress bar), you can do so in didReceiveData.or look at this

NSURLConnection 已经是异步的。只需实现委托方法。如果您想更新 MainThread 上的 UI(例如进度条),您可以这样做didReceiveData.或查看