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

时间:2011-12-08 07:55:02

标签: ios web-services

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

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

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

我需要将来自手机数据库的数据放到远程数据库中,并且在数据成功放入手机数据库中的数据后会被删除。我正在浏览手机数据库中的每个元素并开始连接,这就是为什么我不知道如何从循环中完成下一个操作。当谈到Objective-c编程时,我是初学者,所以我不确定这是否是正确的方法

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

6 个答案:

答案 0 :(得分:19)

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

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

您也可以使用异步连接执行此操作。在这种情况下,您的操作可能会实际完成更快的b / c,您可以同时处理多个请求。如果你这样做,你的循环可以保留在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线程将被解除阻塞并响应。这不是唯一的方法,事实上,我会自己使用异步连接,但这是一个如何获得你想要的简单例子。

答案 1 :(得分: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服务调用时遇到了很多代码,因此将所有内容包装在一个单例中给了我一个很好的清理方式来获取数据。希望这有帮助!

答案 2 :(得分:3)

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

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

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

答案 3 :(得分:1)

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

我建议您查看新的sendAsynchronourRequest方法。我posted up an example of how you can wrap this up in a class会在连接完成/超时/失败时通知其代理人。我只是在推荐你这篇文章,因为听起来你正在努力实现与我当时非常相似的东西,这个DownloadWrapper课程对我来说完美无缺。这是iOS5中的新功能,请注意。

答案 4 :(得分: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]]);

答案 5 :(得分:-1)

NSURLConnection已经是异步的。只需实现委托方法。如果要更新MainThread上的UI(例如进度条),可以在didReceiveData.中执行此操作 或者查看this