使用addOperationWithBlock将块添加到队列并使用完成块

时间:2013-09-16 21:45:47

标签: iphone objective-c multithreading concurrency

我有一个包含3个步骤的过程。每个都需要在另一个之前完成(同步,串行等)。所有这些都需要在后台完成,以免阻止UI。

我正在尝试重新构建一些东西以使用2个队列,一个用于网络操作,一个用于数据库更新以保护核心数据。在队列之间来回反复,我可以保持连续,只需触发一个块,然后在完成后调用一些东西。

我正在使用addOperationWithBlock来创建操作并将其排队,但是没有看到完成块的明显方法(例如我使用setCompletionBlock)。我不确定如何完成第2步。我是否过度思考它,我只是在步骤1的块结束时调用下一个方法(步骤2的起点)?问题是这些块中的内容可能像AFNetworking调用一样异步。

这里有一些代码和更多信息。我想点击服务器,获取数据,然后在完成时执行其他操作,但链接它们以便它必须从数据到验证步骤顺序进行:

self.networkQueue = [NSOperationQueue new];
self.networkQueue.maxConcurrentOperationCount = 1;
self.databaseQueue = [NSOperationQueue new];
self.databaseQueue.maxConcurrentOperationCount = 1;

[self.networkQueue addOperationWithBlock:^{

        NSString *listURL = [NSString stringWithFormat:GET_LIST,BASE_URL];
        NSURL *url = [NSURL URLWithString:briefListURL];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];

        AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest: request
                                                                                            success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
                                                                                                self.list = [NSArray arrayWithArray:(NSArray *)JSON];                                                                                                    
                                                                                            } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
                                                                                                [self listOperationDidFail];
                                                                                            }];

        // define block that will execute when the task is finished
        [operation setCompletionBlock:^{
            // Latest data retrieved. Check if db needs updating
            [self verifyList];
        }];

        [operation start];
    }];

2 个答案:

答案 0 :(得分:1)

使用您的代码会更容易回答,但您可以使用块内的嵌套调用以串行方式异步执行...

- (void)doThreeAsynchThingsSeriallyThenInvoke:(void (^)(void))finished {

    [self doTheFirstThingThenInvoke:^(id result, NSError *error) {
        if (!error) {
            [self doTheSecondThingWith:result thenInvoke:^(BOOL success) {
                [self doThLastThingThenInvoke:finished];
            }];
        }
    }];
}

编辑 - 为了进一步说明,让我们说doTheFirstThing是关于进行网络调用然后解析结果:

- (void)doTheFirstThingThenInvoke:(void (^)(id, NSError *))finished {

    NSURLRequest *request = // form a request, etc.
    [NSURLConnection sendAsynchronousRequest:request queue:someQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        id parsed = nil;
        if (!error) {
            parsed = // parse the result contained in NSData *data
        } else {
            // handle error
        }
        // here's the important part: invoke the completion block either way
        // it will get either nil and an error, or a parsed result and nil
        finished(parsed, error);
    }];
}

...让我们说doTheSecondThing用于在数据库中存储数据:

- (void)doTheSecondThingWith:(id)parsedData thenInvoke:(void (^)(BOOL))finished {

    // you can do something asynch here, maybe on an operation queue
    // or some other way off the main.  Let's say it's an animation, because
    // that takes a BOOL block and we can demonstrate just passing our
    // block param along

    [UIView animateWithDuration:3.0
                     animations:^{ self.someView.alpha = 0.0 }
                     completion:finished];

    // see?  we passed the finished block directly to the animation
    // it will be invoked after the animation is complete

总之,第一件事(网络请求)发生了asynch,离开main,并在完成后调用了一个块。该块调用了第二个东西(动画),它将它的块传递给了一个aysnch操作。第三件事只会在网络请求和动画(两者都跑掉主要部分)完成后才会启动。

答案 1 :(得分:1)

这里真正的问题是队列中块的完成块中的异步过程。

这个食谱应该有所帮助:

  1. 创建NSOperationQueue的实例。设置maxConcurrentOperationCount = 1。

  2. 声明所有块进程及其完成块。放置你想要的任何东西。在每个块处理的开始处,放置一段代码来暂停创建的NSOperationQueue。在每个块的完成块结束时,放置一块取消挂起相同队列的代码 - 如果在块或其完成块中调用异步进程,则需要在该异步进程结束时放置/调用该未释放的代码。 / p>

  3. 将所有块添加到NSOperationQueue。

  4. 您还可以通过将每个流程嵌套在其他流程完成块中来完成工作。