20
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Swift】キャプチャ・リスト

Last updated at Posted at 2020-10-31

#この投稿は何?
Swiftプログラミングにおけるキャプチャ・リストについて、公式ドキュメントの内容を独自に翻訳したものです。

###環境
macOS 10.15.7
Xcode 12.1
Swift 5.3

#キャプチャ・リストとは
既定の動作として、クロージャ式は「その周囲のスコープにある定数および変数」を強い参照によってキャプチャします。キャプチャリストを使用すると、クロージャで値をキャプチャする方法を明示的に制御することができます。

キャプチャ・リストは、パラメータのリストの前に、「角括弧で囲まれたカンマで区切ったリスト」として記述されます。キャプチャ・リストを使用する場合、パラメータ名、パラメータおよび戻り値の型を省略していても、inキーワードを常に使用します。

{ [value1, value2, ...] in STATEMENT }

キャプチャ・リストのエントリは、クロージャ作成時に初期化されます。キャプチャ・リストの各エントリとなる定数は「周囲のスコープで同じ名前を持つ定数または変数」の値に初期化されます。
例えば、以下のコードでは、aはキャプチャリストに含まれていますが、bは含まれていません。

var a = 0
var b = 0
let closure = { [a] in    // aは0で初期化される
    print(a, b)           // スコープ外でaを変更しても、影響しない
}

a = 10                    // この変更は、キャプチャ・リストのaに影響しない
b = 10
closure()
// Prints "0 10"

スコープ内のa は、クロージャが作成されたときに「スコープ外のaの値」で初期化されますが、それらの値は特に繋がっているわけではありません。つまり、スコープ外のaを変更しても、スコープ内のaには影響しませんし、クロージャ内のaへの変更がクロージャ外のaに影響することもありません。対照的に、スコープ外にはbという名前の変数が1つしかないので、その変更はクロージャの内側と外側のどちらにも影響します。

ただし、キャプチャされた変数が「参照型のデータ」だった場合、この区別はありません。
例えば、以下のコードでは、「スコープ外にある変数」と「スコープ内にある定数」に、の2つのxがありますが、どちらも参照型データなので同じオブジェクトを参照しています。

class SimpleClass {
    var value: Int = 0
}
var x = SimpleClass()          // ひとつめの変数x(x.valueは0で初期化される)
var y = SimpleClass()
let closure = { [x] in         // ふたつめの定数x(x.valueは0で初期化される)
    print(x.value, y.value)    // スコープ外でxを変更すると、影響する
}

x.value = 10                   // スコープ外での変更が、キャプチャ・リストのxにも影響する
y.value = 10
closure()
// Prints "10 10"

式の値の型がクラスの場合、式の値への「弱い参照」をキャプチャするために、キャプチャ・リストで式をweakでマークできます。また、「所有されていない参照」をキャプチャするためには、キャプチャ・リストで式をunownedでマークできます。

myFunction { print(self.title) }                    // 「強い参照」による値のキャプチャ(暗黙的)
myFunction { [self] in print(self.title) }          // 「強い参照」による値のキャプチャ(明示的)
myFunction { [weak self] in print(self!.title) }    // 「弱い参照」による値のキャプチャ
myFunction { [unowned self] in print(self.title) }  // 「所有しない参照」で値をキャプチャ

また、キャプチャ・リスト内の値に名前を付けて、任意の式をバインドすることもできます。「バインドした式」はクロージャが作成されたときに評価され、値は指定された強度でキャプチャされます。
例えば、以下のようになります。

// "parent"として、弱い参照で"self.parent"をキャプチャする
myFunction { [weak parent = self.parent] in
    print(parent!.title) 
}

クロージャ式の詳細と例については、クロージャ式を参照してください。
キャプチャリストの詳細と例については、クロージャの強い参照サイクルの解決を参照してください。

20
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?