中学生でもわかる、Swiftのキャプチャリスト
1. キャプチャリストって何?
キャプチャリストとは、クロージャ(後で実行されるコードのまとまり)内で使われる変数の「コピーの仕方」を指定するための機能です。
例えば、クロージャの外で宣言された変数をクロージャの中で使いたいとき、Swiftはその変数を「キャプチャ」します。このキャプチャの方法を調整するためにキャプチャリストが使われます。
2. なぜキャプチャするの?
Swiftのクロージャは、変数をクロージャの外から持ってくることができます。例えば、以下のようなコードがあります:
var name = "Taka"
let greeting = {
print("Hello, \(name)!")
}
この場合、greeting
クロージャは name
をキャプチャして、後で greeting()
が呼ばれると name
の中身を使って「Hello, Taka!」と表示します。
でも name
の値が変わったらどうなるでしょうか?
name = "Yuta"
greeting() // -> "Hello, Yuta!"
ここで気づくのは、name
は変更されても新しい値を使っているということです。このように、クロージャは変数をそのまま参照しているんです。
3. キャプチャリストの使い方
しかし、場合によっては、変数の「コピー」を保持したいときや、メモリの扱いを制御したいこともあります。ここで登場するのがキャプチャリストです。
キャプチャリストは、クロージャ内で使う変数の参照方法を変更するために使います。実際には、変数の前に []
で囲んで指定します。
var name = "Taka"
let greeting = { [name] in
print("Hello, \(name)!")
}
name = "Yuta"
greeting() // -> "Hello, Taka!"
この場合、キャプチャリスト [name]
を使うことで、name
のコピーがクロージャの中に保存され、後から name
が変更されてもクロージャは「コピーされた古い値」を使います。
4. 実際の利用パターン
(1) メモリ管理
クロージャが変数を強く(しっかりと)キャプチャしてしまうと、メモリが解放されずにアプリがメモリを無駄に使ってしまうことがあります。これを避けるために、キャプチャリストで weak
(弱い参照)を使います。
class Person {
var name = "Taka"
func greet() {
let greeting = { [weak self] in
print("Hello, \(self?.name ?? "someone")!")
}
greeting()
}
}
ここでは self
を weak
としてキャプチャすることで、Person
オブジェクトがメモリから解放されることを防ぎます。もし self
をそのまま強くキャプチャしてしまうと、Person
がメモリから消えなくなってしまう可能性があるんです。
(2) 値を一度だけキャプチャしたいとき
値を変更されないように、クロージャの中で「その時の状態」を使いたい場合にもキャプチャリストを使います。例えば、タイマーや遅延した処理を実行するときです。
var counter = 10
let countdown = { [counter] in
print("Countdown: \(counter)")
}
counter = 5
countdown() // -> "Countdown: 10"
[counter]
とすることで、countdown
クロージャが呼ばれたときには「最初にクロージャが作られた時の値(10)」を使います。これで、counter
が後から変更されても、クロージャ内の値は変わりません。
(3) クロージャとUIの連携
例えば、SwiftUIでボタンをタップした時に何かを変える場合、クロージャ内でUIの状態を変更することがあります。キャプチャリストで状態管理をすると、その時のUIの状態をしっかり覚えてくれます。
struct ContentView: View {
@State private var count = 0
var body: some View {
Button("Tap me") {
[count] in
print("Button tapped when count was \(count)")
}
}
}
この例では、Button
がタップされた時に、ボタンが押された時の count
の値を記録しています。
まとめ
キャプチャリストは、クロージャの中で変数をどのようにキャプチャするかをコントロールするために使います。特に、メモリ管理や状態の保持などで役立つ場面が多いです。
簡単にまとめると:
- キャプチャリストを使うと、変数の参照方法を制御できる。
- メモリリークを防ぐために
weak
やunowned
を使うことがある。 - 変数の変更をクロージャの中で反映させないように、値をコピーしてキャプチャすることもできる。
これでキャプチャリストがどういうものか、どう使われるかのイメージがついたかな?
補足のコード
let names = ["Taka", "Nana", "Lynn"]
var name = "Taka"
var greetings: [() -> ()] = []
for newName in names {
name = newName
greetings.append({
print("Hello, \(name)")
})
}
for greet in greetings {
greet()
}
Hello, Lynn
Hello, Lynn
Hello, Lynn
最終的にname = "Lynn"
のため
for newName in names {
name = newName
greetings.append({ [name] in // Capture
print("Hello, \(name)")
})
}
Hello, Taka
Hello, Nana
Hello, Lynn
その都度、値をキャプチャーする。
Discussion