オブザーバーの安全な削除
古いオブザーバーによるリークやクラッシュを防ぎます。
「オブザーバーの安全な削除」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全4レッスンが含まれています。
オブザーバーを安全に削除する
不要になったオブザーバーは削除する必要があります。残ったオブザーバーが所有者の破棄後に実行されると、バグの原因になります。古いAPIではクラッシュにつながることもあります。
削除が重要な理由
セレクタによるオブザーバーは、解放済みのオブジェクトにメッセージを送らないよう、従来はdeinitで削除する必要がありました。
import Foundation
class Watcher: NSObject {
override init() { super.init() }
deinit { NotificationCenter.default.removeObserver(self) }
}
print("Removes itself on deinit")removeObserver(self)
removeObserver(self)を呼び出すと、そのオブジェクトが登録したすべての監視を1回で解除できます。
import Foundation
class VM: NSObject {
func stop() { NotificationCenter.default.removeObserver(self) }
}
VM().stop()
print("Removed all observations for self")特定の名前を削除する
名前とオブジェクトを渡せば、1つの監視だけを削除し、ほかの監視はそのまま残せます。
import Foundation
let center = NotificationCenter.default
let obj = NSObject()
let name = Notification.Name("ping")
center.removeObserver(obj, name: name, object: nil)
print("Removed a specific observation")トークンによるオブザーバー
クロージャベースのaddObserverは、不透明なトークンを返します。そのトークンを保持し、トークンを使ってオブザーバーを削除します。
import Foundation
let center = NotificationCenter.default
let name = Notification.Name("ping")
let token = center.addObserver(forName: name, object: nil, queue: nil) { _ in }
center.removeObserver(token)
print("Removed via token")トークンを保存する
監視を続けたい期間だけトークンが存続するよう、プロパティに保存します。監視が不要になったら削除します。
import Foundation
class Model {
var token: NSObjectProtocol?
func start() {
token = NotificationCenter.default.addObserver(
forName: Notification.Name("x"), object: nil, queue: nil) { _ in }
}
func stop() {
if let t = token { NotificationCenter.default.removeObserver(t) }
}
}
let m = Model(); m.start(); m.stop()
print("Lifecycle managed")deinitによる自動クリーンアップ
deinitでトークンを削除すると、所有者が解放されたときに監視も終了します。
import Foundation
class Owner {
var token: NSObjectProtocol?
deinit { if let t = token { NotificationCenter.default.removeObserver(t) } }
}
print("Token removed on deinit")最新のランタイムに関する注意
最近のランタイムでは、ブロックAPIで追加したオブザーバーはトークンが解放されると自動的に削除されます。ただし、明示的に削除するほうが安全で分かりやすい方法です。
import Foundation
// Holding the token in a property controls its lifetime.
class C { var token: NSObjectProtocol? }
print("Token lifetime equals observation lifetime")強い参照サイクルを避ける
クロージャオブザーバーがselfを強くキャプチャする場合は、解放を妨げる強い参照サイクルを避けるために[weak self]を使います。
import Foundation
class VM {
var token: NSObjectProtocol?
func start() {
token = NotificationCenter.default.addObserver(
forName: Notification.Name("x"), object: nil, queue: nil) { [weak self] _ in
self?.handle()
}
}
func handle() { print("handled") }
}
VM().start()二重削除は安全
すでに削除されたオブザーバーを再度削除しても問題ありません。そのため、防御的なクリーンアップでクラッシュすることはありません。
import Foundation
let center = NotificationCenter.default
let obj = NSObject()
center.removeObserver(obj)
center.removeObserver(obj) // no harm
print("Safe to remove twice")ベストプラクティスのまとめ
トークンベースのクロージャを優先し、トークンを保存してdeinitで削除し、selfは弱くキャプチャします。これにより、監視のライフタイムを正しく管理できます。
import Foundation
class Best {
var token: NSObjectProtocol?
deinit { token.map { NotificationCenter.default.removeObserver($0) } }
}
print("Followed best practices")理解度チェック
addObserver(forName:...)で追加したクロージャベースのオブザーバーは、どのように削除しますか?
まとめ
不要になったオブザーバーは必ず削除します。セレクタによるオブザーバーにはremoveObserver(self)を使い、クロージャによるオブザーバーには返されたトークンを使います。トークンを保存し、deinitでクリーンアップし、selfは弱くキャプチャします。次は、オブザーバーパターンと代替手段を比較します。
よくある質問
「オブザーバーの安全な削除」レッスンは無料ですか?
はい。「オブザーバーの安全な削除」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全4レッスンが含まれています。
「オブザーバーの安全な削除」で何を学びますか?
古いオブザーバーによるリークやクラッシュを防ぎます。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Swift Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「オブザーバーの安全な削除」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このSwift Academyレッスンでコードを書いて実行できますか?
はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。