知りたいこと
guard let self = self else { return } の意味を知りたい
DevelopersIOさんのこのページの「実装」の部分のコードで
guard let self = self else { return }
を見つけたのですが、意味がよく理解できませんでした。
このインスタンスでなければreturnするだと思うのですが、
外部からの侵入を防ぐといったセキュリティの意味合いで書いているのでしょうか?
すみませんが、よろしくお願いします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答1件
0
ベストアンサー
初心者にはかなりヘビーな内容になります。 覚悟してください。
##短い回答
[weak self] でキャプチャしたselfがクロージャ実行時に存在しているかどうかを確認しています。
##とんでもなく長い回答
guard let else
まず、
swift
1guard let xxx = yyy else { return }
はyyyがOptionalで、yyyに値がなければreturnして値があれば xxx にその値を入れる、という構文です。
例えば以下のように動きます。
swift
1func hoge(_ value: Int?) { 2 gurad let v = value else { return } 3 print(v) 4} 5 6hoge(10) // prints 10 7hoge(nil) // no prints
###変数のキャプチャ
次に、関数による変数のキャプチャというものがあります。
swift
1var val = 0 2 3func fuga() { 4 val += 2 // valをキャプチャ 5} 6 7print(val) // prints 0. 8 9fuga() 10print(val) // prints 2.
関数にキャプチャされた変数valは関数内で値を変更することが可能です。
Swiftでは関数とクロージャは同等のものですので以下でも同じです。
swift
1var val = 0 2 3let fuga = { val += 2 }
###インスタンスのオーナーシップ
最後にクラスのインスタンスのオーナーシップです。
クラスのインスタンスにはデータ領域がありますので、そのインスタンスを使わなくなった時にその領域が解放されなければメモリがひっ迫してしまいます。
しかし、使用中に開放されてしまうと、アプリケーションがクラッシュする事態になります。
このためSwiftではインスタンスにオーナーシップというものを設定しこれに対処しています。
原則として、使用中はオーナーを設定し使用が終わるとそれを外します。オーナーがすべてなくなった段階でインスタンスは解放されます。
これらは自動的に行われており、通常はプログラマは何も考える必要もありません。
ただし、循環参照という状態になるとインスタンスが解放されなくなるため、これを対処する必要があります。
swift
1class A { 2 let b: B 3 4 init(b: B) { 5 self.b = b 6 b.a = self 7 } 8} 9 10class B { 11 var a: A? 12} 13 14let a: A? = A(b: B())
この例は意味のあるコードではありませんが、循環参照の典型的な例です。
class Aのインスタンスはclass Bのインスタンスを、class Bのインスタンスはclass Aのインスタンスを参照しています。
そのため、上の例でa = nil
としてもa, b どちらのオーナーもなくならないため、どちらも解放されません。
この循環参照を回避するためにweak
という修飾子が用意されています。
上の例ではclass Bの変数aをweakとします。
swift
1class B { 2 weak var a: A? 3}
このweak
という修飾子は「インスタンスは参照するがオーナーとしては登録しない」という指示を与えます。
こうすることでa = nil
が実行されると、aのオーナーがすべてなくなりaが解放され、それによってbのオーナーもなくなるためbも解放されるようになります。
###キャプチャによるオーナシップの獲得
まだまだ続きます。
つぎは今までできたことの複合技、キャプチャによるオーナシップの獲得です。
これは名前の通りです。先ほどのclass Aを使います。
swift
1var a: A? = A(b: B()) 2 3let f = { print(a) } // インスタンスaをキャプチャ 4 5a = nil // インスタンスaは解放されない
クロージャがインスタンスaをキャプチャした時、自動的にオーナーが設定されるため、a = nil
を実行してもインスタンスaは解放されません。
これにも循環参照の問題が発生します。
swift
1class C { 2 var f: (() -> Void)? 3} 4 5let c: C? = C() 6 7c?.f = { print(c) } 8 9c = nil // c は解放されない
このようにインスタンスcが持つクロージャがcをキャプチャしてしまったため、循環参照が発生し、cが解放されなくなってしまいます。
この時もweak
修飾子を使います。ただし、クロージャ自体をweak参照することができないため以下のように行います。
swift
1c?.f = { [weak c] in print(c) }
このweakはcをキャプチャする時にオーナーを設定するなという指示になります。
この指定をすることでc = nil
の時にインスタンスcが解放されるようになります。
とこらが、これでも問題が発生する場合があります。class Aを使います。
swift
1var a: A? = A(b: B()) 2 3let f = { [weak a] in print(a) } 4 5a = nil 6 7f() // prints nil
aが解放された後にクロージャfを実行しようとすると、クロージャ内で使用しているaがすでに存在していないためnilになってしまいます。
###キャプチャしたインスタンスの存在確認
では、質問の回答です。
この問題を解決するためには実行前にインスタンスaが存在することを確認する必要があります。
クロージャfを以下のようにします。
swift
1let f = { [weak a] in 2 gurad let a = a else { return } 3 print(a) 4}
まず、gurad let a = a else { return }
でインスタンスaの存在を確認し、その後それを使用するようにします。
ご質問のgurad let self = self else { return }
も同じことです。
[weak self] でキャプチャしたselfがクロージャ実行時に存在しているかどうかを確認しています。
投稿2019/06/24 07:32
編集2019/06/24 09:07総合スコア3391
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/06/24 14:28