objective-c,可以排队异步NSURLRequests吗?

时间:2014-04-11 20:25:01

标签: ios objective-c asynchronous nsurlrequest

我意识到这个问题听起来很矛盾。我在应用程序中发出了几个异步请求。情况是第一个异步请求是身份验证请求,其余的将使用成功身份验证请求返回的访问令牌。

两个明显的解决方案是:

  1. 将它们全部同步运行,并冒险使用UI块。 (糟糕的选择)
  2. 运行它们异步,并将请求2-N放入第一个完成处理程序中。 (不实用)
  3. 麻烦的是,随后的请求可以随时在项目的任何地方处理。失败的情况是,如果在发出第一个身份验证请求后立即调用第二个请求,并且在返回访问令牌之前。

    我的问题是,有没有办法排队异步请求,或者在第一个请求成功返回之前以某种方式说不发布它们?

    编辑:

    为什么(2)不实用:第一个是应用加载时发生的身份验证请求。第二个+可能会立即发生,在这种情况下它是实用的,但它也可能发生在一个完全独立的类或大型应用程序的任何其他部分。我实际上无法将整个应用程序放在完成处理程序中。对API请求的其他访问可以在其他类中随时进行。在发生了许多其他事情之后,即使是1-2天之后。

    解决方案:

    //pseudo code using semaphore lock on authentication call to block all other calls until it is received
    
    // at start of auth
    _semaphore = dispatch_semaphore_create(0)
    
    // at start of api calls
    if(_accessToken == nil && ![_apiCall isEqualToString:@"auth]){
        dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
    }
    
    // at end of auth with auth token
    dispatch_semaphore_signal([[SFApi Instance] semaphore]);
    _accessToken = ...;
    

4 个答案:

答案 0 :(得分:2)

这听起来像是你想要使用NSOperation的依赖

来自apple docs:

  

操作依赖

  依赖关系是以特定顺序执行操作的便捷方式。您可以使用addDependency:和removeDependency:方法为操作添加和删除依赖项。默认情况下,具有依赖关系的操作对象在其所有依赖操作对象完成执行之前不会被视为就绪。但是,一旦最后一个相关操作完成,操作对象就会准备好并能够执行。

请注意,要使其正常工作,您必须继承NSOperation"正确"关于KVO合规性

  

NSOperation类是键值编码(KVC)和键值观察(KVO)兼容的几个属性。根据需要,您可以观察这些属性以控制应用程序的其他部分。

答案 1 :(得分:2)

你不可能两种方式 - NSURLConnection的东西没有内置的序列化。但是,您可能已经通过一些常见的类来汇集所有API请求(可能您不会在整个应用程序中无缘无故地进行原始网络调用)。

您需要在该类中构建基础结构,以防止在第一个请求完成之前执行后续请求。这表明某种串行调度队列可以通过所有请求(包括初始身份验证步骤)。你可以通过依赖的NSOperations来做到这一点,正如其他地方所建议的那样,但它不需要那么明确。将请求包装在一组通用的入口点中将允许您以任何方式在幕后执行此操作。

答案 2 :(得分:1)

使用块...我这样做的两种方式:

首先,一个街区内的街区......

    [myCommKit getPlayerInfoWithCallback:^(ReturnCode returnCode, NSDictionary *playerInfo) {
        if (playerInfo) {
            // this won't run until the first one has finished
            [myCommKit adjustSomething: thingToAdjust withCallback:^(ReturnCode returnCode, NSDictionary *successCode) {
                if (successCode) {
                // this won't run until both the first and then the second one finished

                }
            }];
        }
    }];
    // don't be confused.. anything down here will run instantly!!!!

第二种方法是块内的方法

    [myCommKit getPlayerInfoWithCallback:^(ReturnCode returnCode, NSDictionary *playerInfo) {
        if (playerInfo) {
             [self doNextThingAlsoUsingBlocks];
        }
    }];

无论哪种方式,只要我与服务器进行异步通信,我就会使用块。编写与服务器通信的代码时,您必须以不同的方式思考。您必须按照您想要的顺序强制执行操作,并在执行下一步操作之前等待返回成功/失败。习惯阻止是思考它的正确方法。从启动块到它到达回调并执行内部代码之间可能需要15秒。如果他们不在线或服务器中断,它就永远不会回来。

奖金方式..我有时也会用舞台做事:

        switch (serverCommunicationStage) {
            case FIRST_STAGE:
            {
                 serverCommunicationStage = FIRST_STAGE_WAITING;
                 // either have a block in here or call a method that has a block
                 [ block {
                     // in call back of this async call
                     serverCommunicationStage = SECOND_STAGE;
                 }];
                 break;
            }
            case FIRST_STAGE_WAITING:
            {
                 // this just waits for the first step to complete
                 break;
            }
            case SECOND_STAGE:
            {
                 // either have a block in here or call a method that has a block
                 break;
            }
        }

然后在你的绘制循环或某处继续调用此方法。或者设置一个计时器,每隔2秒调用一次,或者对你的应用程序有意义。只需确保正确管理阶段。你不想意外地一遍又一遍地调用请求。因此,在进入服务器调用块之前,请确保将阶段设置为等待。

我知道这可能看起来像是一种较老的学校方法。但它运作正常。

答案 3 :(得分:1)

在这种情况下,我总是发现最简单的方法是同步编写代码并首先正确地在UI线程上运行,只是为了调试。然后,将操作移到单独的线程,并确保您处理并发。

在这种情况下,完美的并发机制是信号量;验证操作在完成后清除信号量,所有其他操作都阻塞它。验证完成后,闸门就会打开。

相关函数是来自Grand Central Dispatch文档的dispatch_semaphore_create()和dispatch_semaphore_wait():https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/doc/uid/TP40008079-CH2-SW2

另一个出色的解决方案是创建一个带有障碍的队列

调度屏障允许您在并发调度队列中创建同步点。当遇到屏障时,并发队列会延迟屏障块(或任何其他块)的执行,直到在屏障完成执行之前提交的所有块为止。此时,屏障块自行执行。完成后,队列将恢复其正常执行行为。

看起来你已经使用信号量运行了,很好!