Lesson 66Classes

Deinitializers

What are Deinitializers?

A deinit is called immediately before a class instance is deallocated. Use it to clean up resources like closing files, invalidating timers, or disconnecting from services.

Deinit Syntax

class MyClass {
    deinit {
        // Cleanup code here
    }
}

Common Use Cases

Close Files

Release file handles and streams

Disconnect

Close database/network connections

Stop Timers

Invalidate scheduled timers

Remove Observers

Unsubscribe from notifications

Key Points

  • Classes ONLY (structs don't have deinit)
  • Called automatically by Swift (ARC)
  • No parameters, no parentheses
  • Superclass deinit called automatically after subclass deinit
main.swift
// DEINITIALIZERS
// Called when a class instance is about to be deallocated

// BASIC DEINIT
class FileHandler {
    var filename: String

    init(filename: String) {
        self.filename = filename
        print("Opening file: \(filename)")
    }

    deinit {
        print("Closing file: \(filename)")
        // Clean up resources here
    }

    func read() -> String {
        return "Contents of \(filename)"
    }
}

// Demo deinit being called
func processFile() {
    let handler = FileHandler(filename: "data.txt")
    print(handler.read())
    // handler goes out of scope here
}  // deinit called automatically!

processFile()
// Output:
// Opening file: data.txt
// Contents of data.txt
// Closing file: data.txt

// DEINIT WITH CLEANUP
class DatabaseConnection {
    var host: String
    var isConnected: Bool = false

    init(host: String) {
        self.host = host
        connect()
    }

    func connect() {
        print("Connecting to \(host)...")
        isConnected = true
        print("Connected!")
    }

    func query(_ sql: String) {
        guard isConnected else {
            print("Not connected!")
            return
        }
        print("Executing: \(sql)")
    }

    deinit {
        if isConnected {
            print("Disconnecting from \(host)...")
            isConnected = false
            print("Disconnected!")
        }
    }
}

// DEINIT IN INHERITANCE
class BaseResource {
    var name: String

    init(name: String) {
        self.name = name
        print("BaseResource \(name) allocated")
    }

    deinit {
        print("BaseResource \(name) deallocated")
    }
}

class FileResource: BaseResource {
    var path: String

    init(name: String, path: String) {
        self.path = path
        super.init(name: name)
        print("FileResource \(path) allocated")
    }

    deinit {
        print("FileResource \(path) deallocated")
        // Superclass deinit called automatically after this!
    }
}

func testInheritance() {
    let file = FileResource(name: "Doc", path: "/tmp/doc.txt")
    print("Using \(file.name)")
}

testInheritance()
// Output:
// BaseResource Doc allocated
// FileResource /tmp/doc.txt allocated
// Using Doc
// FileResource /tmp/doc.txt deallocated
// BaseResource Doc deallocated

// PRACTICAL EXAMPLE: TIMER CLEANUP
class GameTimer {
    var timer: Timer?
    var seconds: Int = 0

    init() {
        startTimer()
    }

    func startTimer() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
            self.seconds += 1
            print("Seconds: \(self.seconds)")
        }
    }

    deinit {
        timer?.invalidate()  // Clean up timer!
        timer = nil
        print("Timer cleaned up")
    }
}

// NOTE: deinit is ONLY for classes
// Structs don't have deinit because they're value types

Try It Yourself!

Create a Logger class that opens a log file in init and closes it in deinit. Print messages when each occurs!