0Pricing
Swift Academy · レッスン

キャプチャリストとweak self

[weak self]で循環参照を避けます。

「キャプチャリストとweak self」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全4レッスンが含まれています。

強参照循環のリスク

オブジェクトがselfを強くキャプチャするエスケープするクロージャーを保持すると、お互いが永遠に生存し続けます — これは強参照循環となり、メモリリークを引き起こします。

強参照による循環

クロージャーはselfを強くキャプチャし、selfはクロージャーを保持するため、どちらも解放されません:

class Owner {
    var onEvent: (() -> Void)?
    func setup() {
        onEvent = { print(self.describe()) }  // strong self -> cycle
    }
    func describe() -> String { "Owner" }
}
let o = Owner(); o.setup()
o.onEvent?()  // "Owner"  (but leaks)

キャプチャリスト

クロージャーの先頭にあるキャプチャリスト、{ [weak self] in ... }は、キャプチャする各参照の保持方法を制御します。強参照ではなく、weakまたはunownedを指定できます。

weak self で循環を断つ

[weak self]はselfを、selfを生存させ続けないオプショナルとしてキャプチャします:

class Owner {
    var onEvent: (() -> Void)?
    func setup() {
        onEvent = { [weak self] in
            print(self?.describe() ?? "gone")
        }
    }
    func describe() -> String { "Owner" }
}
let o = Owner(); o.setup()
o.onEvent?()  // "Owner"  (no cycle)

weak によって self はオプショナルになります

[weak self]を使うと、selfはSelf?になるため、アンラップする必要があります:

class Worker {
    var tag = "W"
    lazy var job: () -> Void = { [weak self] in
        guard let self = self else { return }
        print(self.tag)
    }
}
let w = Worker()
w.job()  // "W"

guard let self パターン

最初にselfを再束縛すると、クロージャーの残りの部分では強い非オプショナル参照を使えます:

class Service {
    var name = "S"
    lazy var run: () -> Void = { [weak self] in
        guard let self else { return }
        print(self.name)
        print("still \(self.name)")
    }
}
Service().run()  // "S" / "still S"

weak と unowned の違い

weakはオプショナルを生成し、オブジェクトがすでに存在しない可能性がある場合にも安全です。unownedは非オプショナルで高速ですが、オブジェクトの解放後にアクセスするとクラッシュします。selfがクロージャーより長く生存すると保証できる場合にだけ使ってください。

unowned を使う

クロージャーがselfより長く生存することがない場合は、unownedを選びます:

class Timer2 {
    var ticks = 0
    lazy var tick: () -> Void = { [unowned self] in
        self.ticks += 1
    }
}
let t = Timer2()
t.tick(); t.tick()
print(t.ticks)  // 2

特定のプロパティをキャプチャする

self全体ではなく必要なものだけをキャプチャしてください。そうすることで、循環を完全に避けられる場合があります:

class VM {
    let id = 42
    lazy var printer: () -> Void = { [id] in print(id) }
}
VM().printer()  // 42

エスケープしないクロージャーに weak は不要です

mapのような同期的でエスケープしないクロージャーは循環を引き起こさないため、そこでselfを強くキャプチャしても問題ありません:

class Calc {
    let factor = 3
    func scale(_ xs: [Int]) -> [Int] {
        xs.map { $0 * self.factor }   // safe, non-escaping
    }
}
print(Calc().scale([1, 2]))  // [3, 6]

経験則

selfを参照する保存済みまたはエスケープするクロージャーには、[weak self]を使います。unownedを使うのは、selfが生存していると保証できる場合だけにしてください。エスケープしないクロージャーでは、キャプチャリストを省略します。

class P {
    var n = "P"
    var stored: (() -> Void)?
    func arm() { stored = { [weak self] in print(self?.n ?? "-") } }
}
let p = P(); p.arm(); p.stored?()  // "P"

クイックチェック

保存されたエスケープするクロージャーで[weak self]を使うのはなぜですか?

まとめ

キャプチャリストについて学びました:

  • 保存されたエスケープするクロージャー内でselfを強くキャプチャすると、強参照循環(メモリリーク)が発生します
  • [weak self]で循環を断てます。selfはオプショナルになるため、guard let selfを使います
  • unownedは非オプショナルで高速ですが、selfが存在しないとクラッシュします
  • エスケープしないクロージャーにキャプチャリストは不要です

次は、オートクロージャーです。

よくある質問

「キャプチャリストとweak self」レッスンは無料ですか?

はい。「キャプチャリストとweak self」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全4レッスンが含まれています。

「キャプチャリストとweak self」で何を学びますか?

[weak self]で循環参照を避けます。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Swift Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「キャプチャリストとweak self」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSwift Academyレッスンでコードを書いて実行できますか?

はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 参照による値のキャプチャ
  2. エスケapingクロージャ
  3. キャプチャリストとweak self
  4. オートクロージャ
← Swift Academyに戻る