ProgrammingiOS開発者

Swiftにおけるクロージャのvalueおよびreferenceキャプチャのメカニズムはどのように機能しますか?キャプチャ時に発生する可能性のある危険は何ですか?また、それをどのように回避できますか?

Hintsage AIアシスタントで面接を突破

回答。

Swiftでは、クロージャは周囲のコンテキストから値や参照を「キャプチャ」することができます。キャプチャされる値が値型(struct、enum)の場合、クロージャはデータをコピーします。参照がキャプチャされた場合(例えばクラス)、クロージャはクラスのインスタンスへの参照を保持しますが、[weak self]や[unowned self]を使用しないとretain cycleが発生する可能性があります。

キャプチャメカニズムは、非同期タスクやコールバックを実装するために役立ちますが、不適切な使用はメモリ管理を妨げます。

例:

class MyClass { var value = 10 func doSomething() { let closure = { [weak self] in print(self?.value ?? "nil") } closure() } }

この例では、非同期処理が行われるため、強い参照サイクルを回避するために[weak self]を使用しています。

ひっかけ問題。

質問:「クロージャでselfをキャプチャする際、必ず[weak self]を使用することでメモリリークを防げますか?」

回答: いいえ、クロージャ内に強い参照が発生する場合(例えば中間変数を介して)、[weak self]を使用してもretain cycleが発生する可能性があります。例えば:

let closure = { [weak self] in let strongSelf = self strongSelf?.doSomething() }

クロージャが長期間生存するオブジェクト(例えばNotificationCenter)にバインドされた場合、selfに対して他の参照が存在するとretain cycleを引き起こす可能性があります。

このトピックの詳細を知らなかったための実際のエラーの例。


物語1

iOSアプリケーションのプロジェクトで、開発者がネットワークリクエストにクロージャを渡す際に[weak self]を指定し忘れました。その結果、リクエストが実行されている間、ビューコントローラーのオブジェクトは解放されず、ユーザーが画面を閉じてもメモリリークが発生しました。


物語2

タイマーにサブスクリプトする複雑なシステムでは、weakを使用せずにクロージャを介してオブジェクトがサブスクリプションを行い、画面がメモリから削除された後も動作を続けました。これにより、もはや存在しないUI要素が再び呼び出され、アプリケーションがクラッシュしました。


物語3

データキャッシュの実装時に、クロージャがキャッシュの長生きするオブジェクト内で[unowned self]のアノテーションなしでselfへの参照をキャプチャしました。元のオブジェクトが破棄された後、クロージャからselfを参照しようとすると、すでに解放されたメモリへのアクセスが発生し、アプリケーションがクラッシュしました。