Нет, в этих шаблонах нет ничего «плохого» по своей сути.
Как было сказано, типичная картина:
workerQueue.async {
// do something computationally intensive
DispatchQueue.main.async {
// update UI and/or model
}
}
И это мотивировано тем, что «у меня есть что-то достаточно интенсивное, что я не хочу, чтобы оно работало в основной очереди (и отрицательно сказывалось на UX), но когда я закончу, мне нужно обновить свой пользовательский интерфейс и / или модель. "
Довольно редко у вас также есть дополнительный уровень вложенности, который говорит: «и когда я закончу обновление пользовательского интерфейса, мне нужно отправить что-то еще обратно в мою рабочую очередь», как показано в вашем первом примере. Если у вас есть практический пример этого, возможно, вы можете поделиться им с нами, поскольку могут быть более изящные способы рефакторинга, чтобы избежать «башни» вложенных отправлений, что может немного усложнить выполнение кода.
Во втором примере, когда вы сначала отправляете в основной поток, тоже немного нетипично. (Да, нам иногда приходится это делать, но это происходит не так часто.) Я полагаю, вы предполагаете, что уже находитесь в каком-то другом потоке (хотя и не всегда). Если это тот случай, когда код ожидает, что вы находитесь в определенном потоке, вы можете сделать это предположение явным, например ::
func foo() {
dispatchPrecondition(condition: .onQueue(workerQueue))
// do some stuff that should be on the `workerQueue`
DispatchQueue.main.async {
// update UI, etc.
...
}
}
В итоге, нет абсолютно никаких проблем с произвольным уровнем вложенности, особенно если вы делаете это с async
... выполнение этого с sync
может вызвать проблемы тупиковой ситуации, если вы не будете осторожны. Но из практического «могут ли будущие программисты прочитать этот код и четко определить, каким будет конечное поведение», вы часто захотите ограничить это, чтобы это не слишком запутывало.
В качестве практического примера я мог бы что-то делать в какой-то выделенной функциональной очереди (например, в очереди доступа к базе данных или в очереди обработки изображений), и мне может потребоваться использовать другую очередь синхронизации для обеспечения безопасного доступа к потокам, и мне может понадобиться делать обновления пользовательского интерфейса в основной очереди. Но у меня обычно нет этой башни с тремя уровнями очередей, но, например, я инкапсулирую эти различные уровни, чтобы избежать путаницы (например, у меня есть универсальный объект для чтения-записи, который инкапсулирует детали этой параллельной очереди, которую я использую для потока- безопасный доступ), и мой код избегает сложного смешивания различных типов очередей отправки в любом данном методе. На любом конкретном уровне абстракции я стараюсь избегать слишком большого количества разных типов очереди одновременно.