В каком порядке вызываются defer блоки в Swift
Что проверяют
Знаете ли вы порядок и точный момент выполнения defer, а не только то, что он «срабатывает в конце». Следом дают кусок кода с defer и return и просят сказать, что вернёт функция, или спрашивают про defer внутри цикла.
Как отвечать
Блоки defer выполняются в обратном порядке объявления, как стек: объявленный последним срабатывает первым. Так удобно освобождать ресурсы в порядке, обратном захвату: открыли файл, взяли блокировку, отпускаем блокировку, закрываем файл.
Важные детали:
- граница области видимости, а не функции:
deferсрабатывает при выходе из ближайшей области, будь то телоif, итерация цикла или блокdo; - регистрация по месту: блок срабатывает, только если выполнение до него дошло.
defer, объявленный послеreturnили после строки, которая бросила ошибку, не выполнится; - выход любым путём:
return,break,continue, брошенная ошибка. Поэтомуdeferхорошо сочетается сthrows; - после вычисления результата: в
var x = 1; defer { x = 2 }; return xфункция вернёт 1, потому что значение дляreturnвычислено до запускаdefer; - ограничения: из тела
deferнельзя выйти черезreturn,breakилиthrow, ошибки внутри обрабатывают на месте.
Типичные применения: lock.lock(); defer { lock.unlock() }, закрытие FileHandle, сброс isLoading = false в функции с несколькими выходами, парные вызовы startAccessingSecurityScopedResource() и stopAccessingSecurityScopedResource().
Где спотыкаются
Связывают defer с управлением памятью. Это управление потоком выполнения: к ARC он отношения не имеет, объект освобождается, когда исчезает последняя сильная ссылка.
Говорят, что defer выполнится в любом случае. При fatalError, падении приложения или вызове exit он не сработает.
Считают, что defer в цикле выполнится один раз после выхода из цикла. Он привязан к телу цикла и срабатывает на каждой итерации, поэтому тяжёлая работа в таком defer внутри длинного цикла легко становится проблемой производительности.