Какая разница между оператором defer и выражением прямо перед возвратом?
Какая разница между этим:
_ = navigationController?.popViewController(animated: true)
defer {
let rootVC = navigationController?.topViewController as? RootViewVC
rootVC?.openLink(url: url)
}
return
и это:
_ = navigationController?.popViewController(animated: true)
let rootVC = navigationController?.topViewController as? RootViewVC
rootVC?.openLink(url: url)
return
Apple swift guideline говорит: "Вы используете оператор defer для выполнения набора инструкций непосредственно перед тем, как выполнение кода покидает текущий блок кода". Но все же я не совсем понял.
Ответы
Ответ 1
Какая разница между отложенным утверждением и утверждением непосредственно перед возвратом?
Вся разница в мире. Оператор defer
выполняется после возврата! Это позволяет вам выполнять вещи, которые не могут быть выполнены никаким другим способом.
Например, вы можете вернуть значение, а затем изменить его. Apple использует этот трюк довольно регулярно; здесь, например, код из документации последовательности, показывающий, как написать собственную последовательность:
struct Countdown: Sequence, IteratorProtocol {
var count: Int
mutating func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}
}
Если вы написали это как
count -= 1
return count
... это сломалось бы; мы не хотим уменьшать count
и затем возвращать его, мы хотим вернуть count
и затем уменьшить его.
Кроме того, как уже указывалось, оператор defer
выполняется независимо от того, как вы выходите. И это работает независимо от того, выходите ли вы из текущей области видимости, что может вообще не включать return
; defer
работает для тела функции, блока while, конструкции if, блока do и так далее. Единственный return
- не единственный способ выйти из такой сферы! В вашем методе может быть несколько return
, и/или вы можете выдать ошибку, и/или у вас может быть break
, и т.д. И т.д., Или вы можете просто достичь последней строки области видимости; defer
выполняется в каждом возможном случае. Написание одного и того же кода "вручную", чтобы охватить все возможные выходы, может быть очень подвержено ошибкам.
Ответ 2
В вашем примере на самом деле нет разницы, но, пожалуйста, посмотрите на это:
func foo(url: URL) -> Int
let fileDescriptor : CInt = open(url.path, O_EVTONLY);
defer {
close(fileDescriptor)
}
guard let bar = something1() else { return 1 }
guard let baz = something2() else { return 2 }
doSomethingElse(bar, baz)
return 3
}
close(fileDescriptor)
всегда выполняется независимо от того, в какой строке возвращается функция.
Ответ 3
инструкция defer используется для выполнения части кода точно до того, как выполнение отходит от последней области.
Например:
func defer() {
print("Beginning")
var value: String?
defer {
if let v = value {
print("Ending execution of \(v)")
}
}
value = "defer function"
print("Ending")
}
Первая строка, которая будет напечатана: Начало
Вторая строка, которая будет печататься: Ending
И последняя строка, которая будет напечатана: Завершение выполнения функции отсрочки.
Ответ 4
Использование defer
позволяет избежать условной очистки в конце функции.
Рассмотрим следующий пример:
class Demo {
var a : String
init(_ a:String) {
self.a = a
}
func finish() {
print("Finishing \(a)")
}
}
func play(_ n:Int) {
let x = Demo("x")
defer { x.finish() }
if (n < 2) {return}
let y = Demo("y")
defer { y.finish() }
if (n < 3) {return}
let z = Demo("z")
defer { z.finish() }
}
play(1)
play(2)
play(3)
Функция play
создает один, два или три объекта Demo
в зависимости от его параметра и вызывает finish
на них в конце прогона. Если функция возвращается из середины, операторы defer
не выполняются, а finish
не вызывается для объектов, которые никогда не создаются.
Альтернативой этому может потребоваться использование опций:
func play(_ n:Int) {
var x:Demo? = nil
var y:Demo? = nil
var z:Demo? = nil
x = Demo("x")
if (n >= 2) {
y = Demo("y")
}
if (n >= 3) {
z = Demo("z")
}
x?.finish()
y?.finish()
z?.finish()
}
Этот подход помещает все объявления вверху и заставляет вас разворачивать дополнительные опции позже. Код с defer
, с другой стороны, позволяет писать код очистки рядом с кодом, который выполняет инициализацию.
Ответ 5
Guard - Guard проверяет некоторое условие и, если оно оценивается как ложное, выполняется оператор else, который обычно существует как метод.
Defer - Defer будет ожидать выполнения блока кода, пока не выйдет текущая область (цикл, метод и т.д.).
Ответ 6
Я хотел бы подчеркнуть, что "оператор defer выполняется после возврата"
class Test {
var value: Int = 0 {
didSet {
print("Value is now \(value)")
}
}
func incrementor() -> Int {
defer {
print("In defer")
value = value + 1
}
print("Just before return")
return value
}
}
let test = Test()
test.value = 1
print("Will call incrementor")
let result = test.incrementor()
print("Did increment with result \(result)")
Консольный выход
Value is now 1
Will call incrementor
Just before return
In defer
Value is now 2
Did increment with result 1
Отсрочка вызывается после возврата, следовательно, старое значение 1
было возвращено вместо увеличенного значения 2
.