Вызов с оператором ?? содержит значение слева и выражение справа: когда Swift вычислит правую часть?
Правая часть оператора ?? вычисляется только тогда, когда левый Optional содержит nil. Если слева есть значение, Swift возвращает его, а выражение справа не выполняет.
Optional отделяет наличие значения от его отсутствия и заставляет явно определить поведение для случая nil. Оператор ?? появился как компактный способ задать безопасное значение по умолчанию без ручной проверки и извлечения Optional.
Такой подход также сохраняет ленивость значения по умолчанию: дорогостоящая операция или побочный эффект справа не запускаются без необходимости.
Если вычислять значение по умолчанию заранее, можно напрасно выполнять сетевой запрос, создавать объект или запускать побочный эффект, хотя исходное значение уже доступно. Это ухудшает производительность и может приводить к неожиданным изменениям состояния.
При неправильном понимании механизма разработчик может решить, что обе стороны оператора вычисляются до выбора результата. Для ?? это неверно: правая часть передаётся как отложенное выражение.
Swift сначала вычисляет левую часть и проверяет Optional. Если там находится значение, оно извлекается и возвращается. Правая часть в этом случае не вычисляется.
Если левая часть равна nil, Swift вычисляет правое выражение и возвращает его результат. Поэтому ?? является оператором с коротким замыканием вычислений, хотя его правая часть выглядит как обычное выражение.
В первом присваивании fallback() не вызывается, потому что cache содержит строку. Во втором вызове cache равен nil, поэтому функция запускается и её результат используется.
Обе части должны быть совместимы по типам: если слева находится T?, результат обычно имеет тип T, а правая часть должна предоставить значение, совместимое с T. ?? не заменяет обработку ошибок и не различает причины отсутствия значения; он только выбирает резервное значение для nil.
В приложении профиль пользователя может быть загружен из памяти. Если кэш пуст, нужно обратиться к хранилищу. Вариант с предварительным вызовом хранилища проще визуально, но выполняет ненужную работу даже при наличии кэшированного профиля.
Ручная конструкция if let даёт полный контроль и удобна, если нужно выполнить несколько действий, обработать ошибки или записать диагностику. Её минус — больший объём кода для простого выбора значения.
Оператор ?? подходит, когда требуется одно резервное значение и не нужна дополнительная логика. Выбор этого решения сохраняет ленивое вычисление и делает намерение очевидным: использовать кэш, иначе загрузить данные.
Вычисляется ли левая часть несколько раз?
Нет, в рамках одного применения оператора выражение слева вычисляется для этого вызова один раз, после чего проверяется полученный Optional. Это важно, если левая часть содержит вызов функции или другой побочный эффект. Однако повторное написание всего выражения с ?? создаст новые вычисления уже на уровне этих отдельных выражений.
Что произойдёт, если правая часть изменяет состояние?
Изменение состояния произойдёт только при nil слева. Например, функция справа может увеличить счётчик или записать сообщение в журнал; при наличии значения в левом Optional этот код не выполнится. Поэтому побочные эффекты справа допустимы лишь тогда, когда такая условность явно ожидаема и понятна из контекста.
Можно ли с помощью ?? отличить nil от другого ошибочного значения?
Нет. Оператор реагирует только на отсутствие значения в Optional. Пустая строка, ноль, false и другие допустимые значения не считаются nil, поэтому для их обработки нужны отдельные условия или другая модель представления состояния.