Lesson 65Classes
Class Initializers
Initializer Types
Classes have two types of initializers: designated (primary) and convenience (secondary). Understanding them is key to class inheritance!
Initializer Types
Designated
Primary init, must initialize ALL properties
init(params) { }Convenience
Secondary init, calls designated init
convenience init() { }Required
Subclasses MUST implement this init
required init() { }Failable
Can return nil if init fails
init?() { }Initializer Rules
- Designated init must call
super.init() - Convenience init must call
self.init() - Convenience init must eventually call designated init
- Initialize own properties BEFORE calling super.init()
Two-Phase Initialization
Phase 1: Initialize all stored properties (bottom-up)
Phase 2: Customize properties, can use self (top-down)
main.swift
// CLASS INITIALIZERS
// Classes have more complex initialization rules than structs
// DESIGNATED INITIALIZERS
// Primary initializer that fully initializes all properties
class Person {
var name: String
var age: Int
// Designated initializer
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
// CONVENIENCE INITIALIZERS
// Secondary initializers that call designated initializers
class User {
var username: String
var email: String
var age: Int
// Designated initializer
init(username: String, email: String, age: Int) {
self.username = username
self.email = email
self.age = age
}
// Convenience initializer
convenience init(username: String) {
self.init(username: username, email: "\(username)@example.com", age: 0)
}
// Another convenience initializer
convenience init(email: String) {
let name = email.components(separatedBy: "@").first ?? "user"
self.init(username: name, email: email, age: 0)
}
}
let user1 = User(username: "john", email: "[email protected]", age: 25)
let user2 = User(username: "jane") // Uses convenience init
let user3 = User(email: "[email protected]")
// INITIALIZER INHERITANCE
class Vehicle {
var brand: String
init(brand: String) {
self.brand = brand
}
convenience init() {
self.init(brand: "Unknown")
}
}
class Car: Vehicle {
var model: String
init(brand: String, model: String) {
self.model = model
super.init(brand: brand)
}
// Override convenience init
override convenience init(brand: String) {
self.init(brand: brand, model: "Base Model")
}
}
let car1 = Car(brand: "Tesla", model: "Model 3")
let car2 = Car(brand: "Toyota") // Uses override
let car3 = Car() // Inherits convenience init!
// REQUIRED INITIALIZERS
class Animal {
var name: String
required init(name: String) {
self.name = name
}
}
class Dog: Animal {
var breed: String
init(name: String, breed: String) {
self.breed = breed
super.init(name: name)
}
// MUST implement required init
required init(name: String) {
self.breed = "Mixed"
super.init(name: name)
}
}
// FAILABLE INITIALIZERS
class Product {
var name: String
var price: Double
init?(name: String, price: Double) {
guard !name.isEmpty else { return nil }
guard price > 0 else { return nil }
self.name = name
self.price = price
}
}
let validProduct = Product(name: "iPhone", price: 999) // Optional(Product)
let invalidProduct = Product(name: "", price: 100) // nil
// TWO-PHASE INITIALIZATION
class Player {
var name: String
var score: Int
init(name: String, score: Int) {
// Phase 1: Initialize all properties
self.name = name
self.score = score
// Phase 2: Can now use self
print("Player \(self.name) created!")
}
}Try It Yourself!
Create a BankAccount class with designated and convenience initializers. Add a failable init that rejects negative balances!