В Swift, если Thread.current.isMainThread == false, тогда безопасно ли один раз выполнять DispatchQueue.main.syn c один раз?
Причина, по которой я спрашиваю, заключается в том, что в приложении моей компании у нас был cra sh, который оказался из-за того, что какой-то метод пользовательского интерфейса вызывался из основного потока, например:
public extension UIViewController {
func presentModally(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
// some code that sets presentation style then:
present(viewControllerToPresent, animated: flag, completion: completion)
}
}
Так как его вызывали из многих мест, некоторые из которых иногда позвоните из фоновой ветки, тут и там были сбои.
Исправить все сайты вызовов было невозможно из-за того, что в приложении было более миллиона строк кода, поэтому я решил просто проверить, находимся ли мы в главном потоке, а если нет, то перенаправить вызов основного потока, например, так:
public extension UIViewController {
func presentModally(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
guard Thread.current.isMainThread else {
DispatchQueue.main.sync {
presentModally(viewControllerToPresent, animated: flag, completion: completion)
}
return
}
// some code that sets presentation style then:
present(viewControllerToPresent, animated: flag, completion: completion)
}
}
Преимущества этого подхода кажутся следующими:
- Сохранение порядка выполнения. Если Вызывающая сторона отключена от основного потока, мы перенаправим на основной поток, затем выполним ту же функцию, прежде чем вернуться - таким образом, сохраняя нормальный порядок выполнения, который был бы, если бы исходная функция была вызвана из основного потока, поскольку функции вызываемый в основном потоке (или любом другом потоке), по умолчанию выполняется синхронно.
- Возможность неявно ссылаться на себя без предупреждений компилятора. В Xcode 11.4 выполнение этого вызова синхронно также удовлетворяет компилятору, который это нормально, чтобы неявно сохранить себя, так как контекст отправки будет введен, а затем выйти до начала Вызов функции inal возвращается - поэтому мы не получаем никаких новых предупреждений компилятора от этого подхода. Это красиво и чисто.
- Больше сфокусированных различий за счет меньшего отступа. Это позволяет избежать оборачивания всего тела функции в замыкание (как вы обычно это делаете, если использовать
Dispatch.main.async { ... }
, где теперь все тело должно быть на более глубоком уровне, что приводит к появлению различий в пробелах в вашем PR, что может привести к раздражающим конфликтам слияний и усложнить рецензентам различение guish существенных элементов в представлениях различий PR в GitHub).
Между тем альтернатива, DispatchQueue.main.async
, может иметь следующие недостатки:
- Потенциально изменяет ожидаемый порядок выполнения. Функция вернется перед выполнением отправленное закрытие, которое, в свою очередь, означает, что
self
могло быть освобождено до его запуска. Это означает, что мы должны явно сохранять себя (или ослаблять его), чтобы избежать предупреждения компилятора. Это также означает, что в этом примере present(...)
не будет вызвано до того, как функция вернется к вызывающей стороне. Это может привести к тому, что модал всплывет после некоторого другого кода, следующего за сайтом вызовов, что приведет к непреднамеренному поведению. - Требование либо ослабления, либо явного сохранения
self
. Это на самом деле не недостаток, но он не настолько чист, стилистически, как способность неявно сохранять себя.
Итак, вопрос в том, верны ли все эти предположения, или я что-то здесь упустил?
Мне показалось, что мои коллеги, которые просматривали PR, использовали DispatchQueue.main.syn c "как-то изначально плохо и рискованно и может привести к тупику. Хотя я понимаю, что использование этого из основного потока действительно приведет к взаимоблокировке, здесь мы явно избегаем этого, используя оператор guard, чтобы убедиться, что мы НЕ в основном потоке. 1048 все еще сохраняют глубокие сомнения относительно этого паттерна, полагая, что он может привести к тупику или неожиданному блокированию пользовательского интерфейса.
Основаны ли эти страхи? Или этот шаблон совершенно безопасен?