1

GCD и блоки такие приятные и удобные. Но когда я влюбился в это, я обнаружил, что случилось что-то плохое. Посмотрите на эти коды ниже:Как избежать вложенных блоков

Crazy? Да. Я в этой беде.

У кого-то есть опыт, избегающий вложенных блоков?

Отредактировано:

Спасибо, ребята. Точно, у нас есть более элегантные способы сделать это. Такие, как:

  • Объявить блоки сперва,
  • делать суб блоки, как самостоятельная функция

Но что я ожидаю, это общее решение. Может быть, как это: (Псевдокод ниже)

functionA.flat.success = [self functionB]; 
functionB.flat.isntnil = [self functionC]; 
functionB.flat.error = {}; 
functionC.flat.isntnil = [self functionD]; 
[flat call:functionA]; 
+0

Я не совсем уверен, какого ответа вы ожидаете. Если вы хотите избежать блокировки, не пишите методы, которые принимают блоки. И даже если они вам нужны, как и для диспетчерской части, в этом нет ничего плохого. – NSAddict

+0

@NSAddict Да. Возможно, я могу перепроектировать функции на уровне бизнес-логики, которые могли бы избежать блокировки ада. Но я все еще хочу знать, есть ли какие-то фреймворки или синтаксический сахар, которые могут выровнять эти асинхронные вызовы. – nickcheng

+0

Вы можете переместить внутреннюю часть каждого блока в отдельный метод, чтобы избежать глубокого (оптического) вложения. –

ответ

5

Ну, я не беспокоили соответствие вашего облако закрытия скобки, но вот попробовать, просто используя возврат, который вы можете свободно использовать внутри блоков слишком и разрезает вложенности немного:

[self functionA:^(BOOL success) { 
    if (!success) 
    return; 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { 
    [self functionB:^(NSError *error) { 
     if (!error) 
     return; 

     dispatch_async(dispatch_get_main_queue(), ^(void) { 
     [self functionC:^(id result) { 
      if (!result) 
      return; 

      [self functionD:^(BOOL success) { 
      if (!success) 
       return; 

      [self DoSomething]; 
      }]; 
     }]; 
     }); 
    }]; 
    }); 
}]; 

Кроме того, никто не заставляет вас писать блоки инлайн, вы можете объявить их как обычные переменные до и использовать их позже. На самом деле, объявляя блоки, прежде чем вы сможете повторного использования них, если ваш API снисходителен к своим пользователям и позволяет вызывается повторно, даже если работа не должно быть сделано:

- (void)foo:(Bar*)bar 
{ 
    // Prepare the success handler. 
    void (^successBlock)(Bar*) = ^(Bar *bar) { 

     [[NSNotificationCenter defaultCenter] 
      postNotificationName:@"barUpdated" 
          object:bar]; 
    }; 

    if (!bar.didAlreadyFetchStuff) { 
     [self wellYouBetterFetchSomething:bar withSuccess:successBlock]; 
    } else { 
     // Oh, fake we already did the work. 
     successBlock(bar); 
    } 
} 

Всякий раз, когда я вижу уровень гнезда слишком высокий я помещаю внутренние блоки как обычные методы в класс и просто вызываю их внутри блока. Эффект тот же, но он выглядит намного чище, и он позволяет вам использовать appledoc или другие инструменты документации для каждого метода, а не надеяться понять беспорядок вложенных недокументированных блоков.

Это только сходит с ума, если вы позволите ему сойти с ума.

+0

Объявление блоков заранее является одним из лучших способов упрощения кода. – Sulthan