Learn SwiftUI (Day 12/100)
Tags: swift
Published: 2024-05-02 04:09:34

## Swift

* class
* inheritance and override
* final class
* init function in subclass
* deep copy
* deinit function
* constant class instance can change var property

```swift
import Cocoa


// ## define class
/*
 * When you copy an instance of a class, both copies share the same data – if you change one copy, the other one also changes.
 */
class Game {
    var score = 0 {
        didSet {
            print("Score is now \(score)")
        }
    }
}

var game1 = Game()
// both game1 and game2 refer to the same object
// classes are reference types in Swift, which means all copies of a class all refer back to the same underlying pot of data.
var game2 = game1
game1.score += 10
print("game1 score:", game1.score) // 10
print("game2 score:", game2.score) // 10

// ## inheritance and override

/*
 This is where Swift enforces a simple rule: if a child class wants to change a method from a parent class, you must use override in the child class’s version
 */

class Employee {
    let hours: Int

    init(hours: Int) {
        self.hours = hours
    }
    
    func summary() {
        print("I'm an employee")
    }
}

class Developer: Employee {
    func work() {
        print("I'm writing code for \(hours) hours.")
    }
    
    override func summary() {
        print("I'm a developer")
    }
}

let john = Developer(hours: 8)
john.summary()

// ## final class
/*
 If you know for sure that your class should not support inheritance, you can mark it as final. This means the class itself can inherit from other things, but can’t be used to inherit from – no child class can use a final class as its parent.
 */

final class Person {
    let name: String
    let age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

// ## init func in sub class
/*
 If a child class has any custom initializers, it must always call the parent’s initializer after it has finished setting up its own properties, if it has any
 */

class Vehicle {
    let isElectric: Bool

    init(isElectric: Bool) {
        self.isElectric = isElectric
    }
}

class Car: Vehicle {
    let isConvertible: Bool

    init(isElectric: Bool, isConvertible: Bool) {
        self.isConvertible = isConvertible
        super.init(isElectric: isElectric)
    }
}

// ## Deep copy

/*
 If you want to create a unique copy of a class instance – sometimes called a deep copy – you need to handle creating a new instance and copy across all your data safely.
 */

class User {
    var username = "Anonymous"

    func copy() -> User {
        let user = User()
        user.username = username
        return user
    }
}

// ## deinitializer

/*
 1. Just like initializers, you don’t use func with deinitializers – they are special.
 2. Deinitializers can never take parameters or return data, and as a result aren’t even written with parentheses.
 3. Your deinitializer will automatically be called when the final copy of a class instance is destroyed. That might mean it was created inside a function that is now finishing, for example.
 4. We never call deinitializers directly; they are handled automatically by the system.
 5. Structs don’t have deinitializers, because you can’t copy them.
 */

// Notice there is no `func` or `()` around `deinit`
class Job {
    deinit {
        print("I quit!")
    }
}

// ## constant class instance can change var property

/*
 This is different from structs, because constant structs cannot have their properties changed even if the properties were made variable.
 structs don’t have the whole signpost thing going on, they hold their data directly. This means if you try to change a value inside the struct you’re also implicitly changing the struct itself, which isn’t possible because it’s constant
 */
```