0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Swift クロージャのキャプチャ(weakが必要なケースと不要なケース)

Last updated at Posted at 2025-03-11

はじめに

Swiftのクロージャがキャプチャする仕組みと、weak を使うべきケースと使わなくてよいケースについて書きます。

クロージャのキャプチャとは?

クロージャは定義されたときに、スコープ内の変数やオブジェクトを 保持(キャプチャ) します。

値型(structlet)のキャプチャ

struct Person {
    let name: String
}

let person = Person(name: "Alice")

let closure = {
    print(person.name) // キャプチャされる
}

closure() // "Alice"

ポイント:

  • structlet の変数は、値のコピーとしてキャプチャされる
  • closure の中で person を変更することはできない

参照型(class)のキャプチャ

class Person {
    var name: String
    init(name: String) { self.name = name }
}

var person = Person(name: "Alice")

let closure = {
    print(person.name) // キャプチャされる
}

closure() // "Alice"

ポイント:

  • class のインスタンスは 参照としてキャプチャ される
  • closure の外で person.name を変更すると、closure 内の person にも影響する

強参照サイクル(メモリリーク)のリスク

クロージャが self をキャプチャすると、強参照サイクルが発生し、メモリリークの原因になることがあります

問題となるコード

class ViewController {
    var title = "Hello"

    func setupClosure() {
        let closure = {
            print(self.title) // self をキャプチャ
        }
        closure()
    }
}

問題点:

  • closureself.title を参照するため、self をキャプチャする
  • self が解放されない可能性がある

解決策: [weak self] を使う

class ViewController {
    var title = "Hello"

    func setupClosure() {
        let closure = { [weak self] in
            print(self?.title ?? "No title") // self を弱参照
        }
        closure()
    }
}

[weak self] の効果:

  • self弱参照 することで、メモリリークを防ぐ
  • selfnil になる可能性があるので、self? で安全にアクセス

3. weak を使うべきケースと使わなくてよいケース

キャプチャが発生する場合 キャプチャしない場合
クロージャの中で self にアクセスしている self を使っていない
クロージャの中でクラスのプロパティを参照している クロージャ内で独立した変数だけを使っている
クロージャが self を長期間保持する クロージャが一時的に実行される

weak を使うべきケース

1. クラスのプロパティとしてクロージャを保持する場合

class ViewController {
    var closure: (() -> Void)?
    
    func setupClosure() {
        closure = { [weak self] in
            print(self?.title ?? "No title")
        }
    }
}

理由:

  • closureself をキャプチャすると、self が解放されない

2. 非同期処理の中で self を参照する場合

class ViewController {
    func fetchData() {
        DispatchQueue.global().async { [weak self] in
            self?.updateUI()
        }
    }
}

理由:

  • ネットワーク通信などで self をキャプチャすると、完了するまで self が解放されない可能性がある

weak を使わなくてよいケース

1. クロージャが即時実行される場合

func doSomething() {
    let closure = {
        print("Hello") // self をキャプチャしない
    }
    closure()
}

理由:

  • closureself に依存しておらず、すぐに実行されるので問題なし

2. クロージャのスコープ内で完結する場合

func performAction() {
    let localVar = "Swift"
    let closure = {
        print(localVar) // キャプチャするが問題ない
    }
    closure()
}

理由:

  • localVarStrin なので、値コピーされるためメモリリークの心配なし

まとめ

クロージャがキャプチャするのは、外部の変数や self にアクセスする場合
class のインスタンスをキャプチャすると、強参照サイクルが発生する可能性がある
[weak self] を使うことで、メモリリークを防ぐ
即時実行や値コピーの場合は weak は不要

クロージャのキャプチャを正しく理解し、適切に weak を使うことで、メモリ管理のバグを防ぐことができます!

0
1
0

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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?