これの違いは何ですか:
_ = 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ガイドラインによると、「コードの実行が現在のコードブロックを離れる直前に、deferステートメントを使用して一連のステートメントを実行します。」、しかしそれでもまだわかりません。
延期ステートメントと復帰直前のステートメントの違いは何ですか?
世界のすべての違い。 defer
ステートメントが実行されますafter return!これにより、他の方法では達成できないことを達成できます。
たとえば、値を返し、thenで値を変更できます。 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
ステートメントはhow終了しても実行されます。また、現在のscopeを終了しても機能します。これは、return
をまったく含まない可能性があります。 defer
は、関数本体、whileブロック、if構成、doブロックなどで機能します。単一のreturn
は、そのようなスコープを終了する唯一の方法ではありません!メソッドに複数のreturn
が含まれている可能性があります。エラーが発生したり、break
などが発生したり、単にスコープの最後の行は当然です。 defer
は、可能なすべてのケースで実行されます。可能なすべての出口をカバーするように「手作業で」同じコードを書くと、エラーが発生しやすくなります。
あなたの例では実際には違いはありませんが、これを見てください:
_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)
は、関数が返す行に関係なく常に実行されます。
deferステートメントは、実行が最近のスコープを離れる直前にコードを実行するために使用されます。
例えば:
func defer() {
print("Beginning")
var value: String?
defer {
if let v = value {
print("Ending execution of \(v)")
}
}
value = "defer function"
print("Ending")
}
印刷される最初の行は次のとおりです。
印刷される2行目:終了
そして、印刷される最後の行は、次のとおりです。defer関数の実行を終了しています。
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
は、パラメーターに応じて1つ、2つ、または3つの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
を含むコードでは、初期化を行うコードの近くにクリーンアップコードを記述できます。