执行同步操作

时间:2013-11-21 19:51:01

标签: ios objective-c cocoa-touch grand-central-dispatch nsoperationqueue

说我想实现这样的模式:

a = some array I download from the internet
b = manipulate a somehow (long operation)
c = first object of b

这些显然需要同步调用,这会导致我在Objective C中遇到问题。我已经阅读了NSOperationQueue和GCD,我不太了解它们,或者哪些适合这里。有人可以建议一个解决方案?我知道我也可以使用performSelector:@selector(sel)WaitUntilDone,但这对于大型操作来说似乎并不高效。

3 个答案:

答案 0 :(得分:5)

所以创建一个串行调度队列,转储那里的所有工作(每个块都在一个块中),最后一个块将一个方法发布回主队列的自己,告诉你的控件类工作已完成。

这是迄今为止很多此类任务的最佳架构。

答案 1 :(得分:2)

我很高兴你的问题得到解答。另外几点意见:

  1. 您术语中的一个小改进,但我假设您想要异步运行这些任务(即不要阻止主队列并冻结用户界面),但您希望这些任务以串行方式运行方式(即每个人在开始下一个任务之前等待前一步骤完成的情况)。

  2. 在进入串行队列之前,最简单的方法是在一个调度任务中执行这三个任务:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self doDownloadSynchronously];
        [self manipulateResultOfDownload];
        [self doSomethingWithFirstObject];
    });
    

    正如您所看到的,由于这是完成这三个步骤之后的所有单个任务,您可以将其分配给任何后台队列(在上面,它是一个全局后台队列),但是因为你“在给定的调度块中完成所有操作,这三个步骤将按顺序执行,一个接一个地执行。

    请注意,虽然创建同步网络请求通常是不可取的,但只要您在后台队列中执行此操作,就不会出现问题(尽管您可能希望创建基于操作的网络请求,如下面的#中所述) 5如果您想享受取消正在进行的网络请求的能力。)

  3. 如果您确实需要单独调度这三个任务,那么只需创建自己的专用串行队列即可。默认情况下,当您创建自己的自定义调度队列时,它是一个串行队列,例如:

    dispatch_queue_t queue = dispatch_queue_create("com.company.app.queuename", 0);
    

    然后,您可以安排这三项任务:

    dispatch_async(queue, ^{
        [self doDownloadSynchronously];
    });
    
    dispatch_async(queue, ^{
        [self manipulateResultOfDownload];
    });
    
    dispatch_async(queue, ^{
        [self doSomethingWithFirstObject];
    });
    
  4. 操作队列方法同样简单,但默认情况下,操作队列是并发的,所以如果我们希望它是一个串行队列,我们​​必须指定没有并发操作(即最大并发操作)操作计数为1):

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1;
    
    [queue addOperationWithBlock:^{
        [self doDownloadSynchronously];
    }];
    
    [queue addOperationWithBlock:^{
        [self manipulateResultOfDownload];
    }];
    
    [queue addOperationWithBlock:^{
        [self doSomethingWithFirstObject];
    }];
    

    这引出了为什么你可以使用操作队列方法而不是GCD方法的问题。主要原因是,如果您需要取消操作的能力(例如,如果用户解雇启动异步操作的视图控制器,您可能希望停止操作),操作队列提供取消操作的能力,而它更麻烦用GCD任务完成。

  5. 在我看来,这里唯一棘手/微妙的问题是你想如何同步进行网络操作。您可以使用NSURLConnection类方法sendSynchronousRequest或仅使用NSData从服务器获取dataWithContentsOfURL。使用这些类型的同步网络请求存在限制(例如,一旦启动就无法取消请求),因此我们中的许多人都会使用基于NSOperation的网络请求。

    正确地执行此操作可能超出了您的问题的范围,因此我可能会建议您考虑使用AFNetworking创建基于操作的网络请求,您可以将其集成到上面的解决方案#4中,从而消除大部分如果您进行了基于NSOperation的网络操作,则需要进行编程。

  6. 要记住的主要事情是,当您在后台队列上运行此类代码时,当您需要进行UI更新(或更新模型)时,这些必须发生在主队列上,而不是后台队列。因此,如果进行GCD实施,您可以:

    dispatch_async(queue, ^{
        [self doSomethingWithFirstObject];
    
        dispatch_async(dispatch_get_main_queue(),^{
            // update your UI here
        });
    });
    

    等效的NSOperationQueue再现将是:

    [queue addOperationWithBlock:^{
        [self doSomethingWithFirstObject];
    
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            // update your UI here
        }];
    }];
    
  7. 关于这些问题的基本原则是Concurrency Programming Guide

    有很多关于此主题的WWDC视频,包括WWDC 2012视频Asynchronous Design Patterns with Blocks, GCD, and XPCBuilding Concurrent User Interfaces on iOS以及WWDC 2011视频Blocks and Grand Central Dispatch in PracticeMastering Grand Central Dispatch

答案 2 :(得分:0)

我会看看反应性可可(https://github.com/ReactiveCocoa/ReactiveCocoa),这是一种很好的方法,可以在不阻塞的情况下将操作链接在一起。您可以在NSHipster中阅读更多相关信息:http://nshipster.com/reactivecocoa/

相关问题