はじめに
Swiftの実装をしていてinitとdeinitについてなんとなくで使っていたのでこの機会に勉強がてらまとめてみました。
initについて
初期化
オブジェクトが生成された時、最初に実行される処理
プロパティに値を設定したり、オブジェクト生成時に必ず実施する処理を記載する。
class Person {
var name: String
init(name: String) {
self.name = name
print("\(name) が生成されました")
}
}
let p = Person(name: "TAKASHI")
// → "TAKASHI が生成されました"
deinitについて
終了処理
インスタンスがメモリから解放される時に呼び出される処理
ファイルを開いたり、変更監視設定を行った場合はメモリが確保された状態になる。
その際に明示的にリソース解放を行わないとメモリ確保された状態が続くためアプリクラッシュやメモリリークにつながる。
例. Realmの変更監視を行う場合
◾️処理概要
Realm(TodoEntity)の変更を監視し、変更されるたびTodoViewに最新情報を表示する。
ここでdeinit内でnotificationToken?.invalidate()
の設定を入れておかないと、、、
TodoViewが破棄された後も変更通知を実施しようとする。
↓
すでに破棄されたTodoViewのメモリ領域にアクセスを試みる。
↓
Viewは破棄済みのためアクセス不可となりアプリクラッシュ or メモリリークが起こる。
// Realmの変更通知設定を行う
class TodoManager: ObservableObject {
private var notificationToken: NotificationToken?
init() {
let realm = try! Realm()
let todos = realm.objects(TodoEntity.self)
// Realmに「監視させて!」ってお願いしてる
notificationToken = todos.observe { changes in
print("データ変わった!")
}
}
deinit {
// Viewが破棄されるとき、監視も止める
notificationToken?.invalidate()
}
}
// Realmの変更検知し最新のリストを表示するView
struct TodoView: View {
var category: TodoEntity.Category
@StateObject private var todoManager: TodoManager
@State var newTask: String = ""
init(category: TodoEntity.Category) {
self.category = category
_todoManager = StateObject(wrappedValue: TodoManager(category: category))
}
var body: some View {
VStack {
List {
ForEach(0..<todoManager.count, id: \.self) { i in
Text(todoManager.todos[i].task!)
}
}
}
}
}