3

我有一个包含 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];
    }];
4

2 回答 2

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

总而言之,第一件事(网络请求)是异步发生的,在主节点之外,并在完成时调用了一个块。该块调用了第二件事(动画),它将它的块传递给 aysnch 操作。第三件事只有在网络请求和动画(两者都跑出主线)完成后才会开始。

于 2013-09-16T23:49:46.830 回答
1

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

这个食谱应该有帮助:

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

  2. 声明所有块进程及其完成块。把你想要的任何东西放在那里。在每个块进程的开始,放置一段暂停创建的 NSOperationQueue 的代码。在每个块的完成块的末尾,放置一段取消挂起相同队列的代码 - 如果您在块或其完成块内调用异步进程,则需要在该异步进程的末尾放置/调用该取消挂起的代码。

  3. 将所有块添加到 NSOperationQueue。

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

于 2013-09-17T00:05:36.667 回答