2013-11-21 6 views
4

У меня есть сетевой код с сильным разбором JSON. Это необходимо сделать в фоновом режиме, чтобы не блокировать основной поток. Код выглядит следующим образом:AFSetworking background JSON parsing avoid block nesting

-(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, поскольку блок завершения партии иногда срабатывал перед всеми отдельными этапами завершения операций ... (Я также читал, что Мэтт не поощрял это делать)

Любые указатели на то, как это сделать лучше?

ответ

1

Я не уверен, что вы хотите здесь, но так же, как «longcat is long», это несколько присуще шаблону: «стиль продолжения - стиль продолжения прохождения». Если вы хотите немного сгладить детали, вы можете сделать локальные переменные блока, но в определенной степени вы застряли, потому что вам нужно завершить для -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]; 
     }); 
    }]; 
} 
+0

Спасибо, я реализовал нечто похожее на параметр 'completeQueue'. Быстрый вопрос: можно ли написать аналогичный код только с 'NSOperation'/'NSOperationQueue', а не блокировать? – userD