AFNetworking后台JSON解析避免了块嵌套

时间:2013-11-21 09:56:52

标签: afnetworking grand-central-dispatch

我有一些网络代码,正在进行大量的JSON解析。它需要在后台完成,以阻止主线程。代码如下所示:

-(void) getSomeDataWithParameters:(...)parameters completion:(void (^)(NSArray *data))completion
{
    NSURLRequest *req = ...
    AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:req];

    // sometimes I have more requests

    // startOperations is a wrapper on AFHTTPClient enqueueBatchOfHTTPRequestOperations:progressBlock:completionBlock:
    // that handles errors and loading views
    [self startOperations:@[op] completionBlock:^(NSArray *operations) {

        // getBgQueue = return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(getBgQueue(), ^{

            NSArray *data = [MyParserClass parseJSON:op.responseJSON inContext:self.localContext];

            [self.localContext MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
                // this is executed on main thread
                if(completion) completion(...);
            }];

        });

    }];
}

(AFNetworking 1.x)

上面的代码工作得非常好,但是设置和编写都很痛苦。通常整个方法内容都包含在另一个块中,以便首先获取一些所需的数据...基本上这些块只是堆积起来并制作丑陋的代码

我正在使用enqueueBatchOfHTTPRequestOperations而不是AFJSONRequestOperation上的单个完成块,因为批处理完成块有时会在所有单个操作完成块之前触发...(我也读过Mattt不鼓励这样做的地方)

有关如何做得比这更好的指示?

1 个答案:

答案 0 :(得分:1)

我不确定你想要什么,但就像“longcat很长”一样,它在模式中有些固有:'延续传递风格是延续传递风格'。如果你想稍微讨论一下,你可以制作局部块变量,但是在某种程度上,你因为-MR_saveToPersistentStoreWithCompletion需要完成才能关闭data以便通过而被卡住了它已完成-getSomeDataWithParameters...,但在执行data完成之前,-startOperations将不存在。

你可以通过使用一堆__block变量,并将代码分成几个本地块来实现较少嵌套的外观,但对我来说,感觉有点像削减你的鼻子来惹恼你的脸。这段代码很容易理解。

顺便说一下......我注意到你在op完成区块中关闭了-startOperations。这很好,因为你通过op加入-startOperations: @[op] ...,但从op参数到完成operations可能会更清晰。我尽可能合理地收紧了这个:

- (void)getSomeDataWithParameters:(...)parameters completion:(void (^)(NSArray *data))completion
{
    NSURLRequest *req = ...;
    AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:req];

    [self startOperations:@[op] completionBlock:^(NSArray *operations) {
        for (AFJSONRequestOperation *op in operations) {
            dispatch_async(getBgQueue(), ^{
                NSArray *data = [MyParserClass parseJSON:op.responseJSON inContext:self.localContext];
                void (^mrSaveCompletion)(BOOL, NSError*) = completion ? ^(BOOL success, NSError *error) { completion(data); } : nil;
                [self.localContext MR_saveToPersistentStoreWithCompletion: mrSaveCompletion];
            });
        }
    }];
}

这会将每个响应分散到不同的线程。如果您希望在单个后台线程上执行所有响应,只需交换for循环和dispatch_async的嵌套。

从那里,唯一真正“多余”的代码是dispatch_async。您可以通过使-startOperations:...获取队列参数来消除这种情况,您可以在队列中传入您希望调用完成的队列。也许是这样的:

- (void)startOperations: (NSArray*)ops completionQueue: (dispatch_queue_t)queue completionBlock: (void (^)(NSArray*))completion
{
    void (^completionWrapper)(NSArray*) = !completion ? nil : ^(NSArray* ops) {
        if (queue)
            dispatch_async(queue, ^{ completion(ops); });
        else
            completion(ops);
    };

    [self startOperations: ops completionBlock: completionWrapper];
}

- (void)getSomeDataWithParameters:(...)parameters completion:(void (^)(NSArray *data))completion
{
    NSURLRequest *req = ...;
    AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:req];
    [self startOperations:@[op] completionQueue: getBgQueue() completionBlock:^(NSArray *operations) {
        for (AFJSONRequestOperation *op in operations) {
            NSArray *data = [MyParserClass parseJSON:op.responseJSON inContext:self.localContext];
            void (^mrSaveCompletion)(BOOL, NSError*) = !completion ? nil : ^(BOOL success, NSError *error) { completion(data); };
            [self.localContext MR_saveToPersistentStoreWithCompletion: mrSaveCompletion];
        });
    }];
}