【问题标题】:Recommended Design Patterns for Asynchronous Blocks?推荐的异步块设计模式?
【发布时间】:2012-07-31 19:10:58
【问题描述】:

我正在开发一个具有高度异步设计的 iOS 应用程序。在某些情况下,单个概念上的“操作”可能会将许多子块排队,这些子块将异步执行并异步接收它们的响应(对远程服务器的调用)。这些子块中的任何一个都可能在错误状态下完成执行。如果任何子块发生错误,则应取消任何其他子块,错误状态应向上渗透到父块,并应执行父块的错误处理块。

我想知道在这样的环境中可以推荐哪些设计模式和其他技巧?

我知道 GCD 的 dispatch_group_async 和 dispatch_group_wait 功能。这可能是这个应用程序设计的一个缺陷,但我对 dispatch_group_async 的运气并不好,因为该组似乎对子块没有“粘性”。

提前致谢!

【问题讨论】:

    标签: ios cocoa-touch cocoa design-patterns


    【解决方案1】:

    有一个 WWDC 视频(2012 年)可能会对您有所帮助。它使用自定义NSOperationQueue 并将异步块放置在NSOperations 中,因此您可以保留块的句柄并取消剩余的排队块。

    一个想法是让子块的错误处理在处理NSOperationQueue的类中调用主线程上的方法。然后班级可以适当地取消其余部分。这样子块只需要知道自己的线程和主线程。这是视频的链接

    https://developer.apple.com/videos/wwdc/2012/

    该视频名为“在 iOS 上构建并发用户界面”。相关部分主要是在后半部分,但你可能想看整个事情,因为它很好地融入了上下文。

    编辑:

    如果可能的话,我建议在一个嵌入块中处理响应,它将它很好地包装在一起,这就是我认为你所追求的......

    //Define an NSBlockOperation, and get weak reference to it
    NSBlockOperation *blockOp = [[NSBlockOperation alloc]init];
    __weak NSBlockOperation *weakBlockOp = blockOp;
    
    //Define the block and add to the NSOperationQueue, when the view controller is popped
    //we can call -[NSOperationQueue cancelAllOperations] which will cancel all pending threaded ops
    [blockOp addExecutionBlock: ^{
    
        //Once a block is executing, will need to put manual checks to see if cancel flag has been set otherwise
        //the operation will not be cancelled. The check is rather pointless in this example, but if the
        //block contained multiple lines of long running code it would make sense to do this at safe points
        if (![weakBlockOp isCancelled]) {
    
            //substitute code in here, possibly use *synchronous* NSURLConnection to get
            //what you need. This code will block the thread until the server response
            //completes. Hence not executing the following block and keeping it on the 
            //queue.  
            __block NSData *temp;
            response = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
    
            [operationQueue addOperationWithBlock:^{
                if (error) {
                      dispatch_async(dispatch_get_main_queue(), ^{
                            //Call selector on main thread to handle canceling
                            //Main thread can then use handle on NSOperationQueue
                            //to cancel the rest of the blocks 
                      });
                else {
                     //Continue executing relevant code....      
                }
            }];
        }
    }];
    [operationQueue addOperation:blockOp];
    

    【讨论】:

    • 谢谢,我看了视频。我想我正在绊倒的是,在等待异步响应时,一个操作如何基本上保留在队列中......?能够利用 NSOperationQueue 会很方便。我之前在其他应用程序中使用过该类,但以前我只让队列处理出站请求 - 而不是响应处理。在此应用程序中,只有在处理完响应并且所有关联的子请求也完成后,操作才会完成。
    • 你能把响应处理代码放在一个嵌入块中吗?我会更新我的答案
    • 如果你在NSOperation 世界,而不是dispatch_async(dispatch_get_main_queue(),^{});,为什么不[[NSOperationQueue mainQueue] addOperationWithBlock:^{}];?你得到的很好,但是将 GCD 调用与NSOperationQueue 调用混合起来感觉很奇怪。
    • 另外,为什么在获得dataWithContentsOfURL 之后,当代码只是要将某些内容分派到主队列时,您是否要将另一个块添加到该块正在运行的同一个队列中。为什么不立即分派到主队列。您的后台队列很可能已备份,但您的主队列未备份。不知道为什么要推迟对错误的处理。
    • 是的,我认为你说得对,它可以用块来完成,但这会使它变得更复杂,因为这里主线程上唯一被调用的是处理来自的错误NSURL 连接。如果没有错误,块内的代码继续执行
    【解决方案2】:

    自从发布此问题以来,我遇到的一种模式是使用信号量将异步操作更改为同步操作。这非常有用。这篇博文更详细地介绍了这个概念。

    http://www.g8production.com/post/76942348764/wait-for-blocks-execution-using-a-dispatch-semaphore

    【讨论】:

      【解决方案3】:

      有很多方法可以在 cocoa 中实现异步行为。

      GCD、NSOperationQueue、performSelectorAfterDelay,创建自己的线程。有适当的时间使用这些机制。在这里讨论太长了,但是您在帖子中提到的一些问题需要解决。

      如果任何子块发生错误,则应取消任何其他子块,错误状态应向上渗透到父块,并执行父块的错误处理块。

      块不能在堆栈中抛出错误。时期。

      【讨论】:

      • 谢谢。在这个应用程序中,我与服务器有一个通信通道,并且客户端/服务器操作是隐式串行的。我不想将任何错误向上传递。
      • 那么你只需要一个异步调用——使用 GCD,或者创建你自己的线程。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-10
      相关资源
      最近更新 更多