クロージャの定義の一部としてキャプチャリストを定義して、クロージャとクラスインスタンス間の強い参照の循環を解決します。キャプチャリストには、クロージャの本体内で 1 つ以上の参照型をキャプチャするときに利用するルールを定義します。2 つのクラスインスタンス間での強い参照の循環と同じように、キャプチャされる参照を、強い参照よりも弱い参照、あるいは非所有の参照として宣言します。弱い参照と非所有の参照のどちらの選択が適切かは、コード間の関係によります。
self
のメンバーを参照するときには(someProperty
や someMethod()
ではなく)self.someProperty
や self.someMethod()
と記述する必要があります。これにより、意図せず self
をキャプチャしてしまう可能性があることを思い出すことができます。
キャプチャリストを定義
キャプチャリストの各項目は、(self
のような)クラスインスタンスや、(delegate = self.delegate!
のように)ある値で初期化される変数に対する参照の weak
や unowned
キーワードのペアです。これらのペアは角括弧内にカンマ区切りで記述されます。
クロージャのパラメータリストと戻り値の型の前にキャプチャリストを置きます。
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// クロージャ本体
}
文脈から推論できるためにクロージャがパラメータリストや戻り値の型を指定していない場合には、クロージャの一番初めにキャプチャリストを置いて in
キーワードを続けます。
lazy var someClosure: () -> String = {
[unowned self, weak delegate = self.delegate!] in
// クロージャ本体
}
弱い参照と非所有の参照
キャプチャするクロージャとインスタンスが常に互いを参照し、常に同時に割り当て解除されるような場合には、非所有の参照としてクロージャ内にキャプチャを定義します。
一方で、キャプチャされた参照がその後 nil
になるような場合には、弱い参照としてキャプチャを定義します。弱い参照は常にオプショナル型で、参照するインスタンスが割り当て解除されたとき自動的に nil
になります。これにより、クロージャの本体内で存在を確認することができます。
nil
になることが無い場合には、弱い参照ではなく、常に非所有の参照としてキャプチャされるべきです。
非所有の参照は、前に見た HTMLElement
の例での強い参照の循環を解決するために使用する適切なキャプチャ方式です。次は、循環を避ける HTMLElement
クラスをどのように記述するかについてです。
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
HTMLElement
のこの実装は、asHTML
クロージャ内のキャプチャリストを追加していることを別にすれば、以前の実装とまったく同じです。このケースでは、キャプチャリストは [unowned self]
で、「強い参照ではなく、非所有の参照として self をキャプチャする」ことを意味します。
前と同じように、HTMLElement
インスタンスを生成して出力します。
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// "<p>hello, world</p>" と出力
次は、キャプチャリストによる参照がどのようになっているかを示しています。
今回は、クロージャによる self
のキャプチャは非所有の参照で、キャプチャしている HTMLElement
インスタンスを強く保持し続けません。paragraph
変数からの強い参照に nil
を設定した場合、次の例でデイニシャライザのメッセージ出力が確認できるように、HTMLElement
インスタンスは割り当て解除されます。
paragraph = nil
// "p is being deinitialized" と出力
キャプチャリストについての詳細な情報は、Capture Lists を確認してください。
Portions of this page are translations based on work created and shared by Apple and used according to terms described in the Creative Commons Attribution 4.0 International License.