TIL: 생성자(Initializer)

Royce·2025년 3월 23일

Swift 문법

목록 보기
34/63

초기화(Initialization)와 생성자(Initializer)

초기화(Initialization)

  • 클래스, 구조체, 열거형의 인스턴스를 생성하는 과정이다
  • 각 저장 속성에 대한 초기값을 설정하여 인스턴스를 사용 가능한 상태로 만드는 과정이다
  • 열거형의 경우 저장 속성이 없으므로 case 중 하나를 선택하여 인스턴스를 생성한다

생성자(Initializer)

  • 생성자(Initializer)의 역할은 인스턴스를 초기화하면서 모든 저장 속성이 유효한 초기값을 가지도록 보장하는 것이다
  • 생성자가 완료되면 인스턴스는 사용 가능한 상태가 된다

생성자(Initializer) 구현

생성자를 구현하는 방법

  1. 모든 저장 속성을 초기값으로 설정해야 한다
  2. 저장 속성을 선언과 동시에 초기화하거나, 옵셔널로 선언할 수 있다 (옵셔널일 경우 기본값은 nil)
  3. 사용자 정의 생성자를 구현하면, 자동으로 제공되는 기본 생성자(init())는 사라진다
  4. 구조체는 자동으로 제공되는 멤버와이즈 이니셜라이저를 사용 가능하다

클래스, 구조체, 열거형에서 동일하게 사용됨 (클래스는 상속이 존재하여 좀 더 복잡함)


생성자 구현의 기본 예제

기본적인 생성자 구현 (클래스)

class Laptop {
    var brand: String
    var model: String
    var price: Double
    var isOn: Bool

    // 생성자 - 모든 저장 속성을 초기화
    init(brand: String, model: String, price: Double, isOn: Bool) {
        self.brand = brand
        self.model = model
        self.price = price
        self.isOn = isOn
    }
    
    // 컴퓨터를 켜는 메서드
    func powerOn() {
        isOn = true
    }
    
    // 컴퓨터를 끄는 메서드
    func powerOff() {
        isOn = false
    }
}

// 인스턴스 생성
let myLaptop = Laptop(brand: "Apple", model: "MacBook Pro", price: 2499.99, isOn: false)
myLaptop.powerOn()
print(myLaptop.isOn)  // true

기본값을 사용한 생성자 구현 (클래스)

class Phone {
    var brand: String = "Unknown"
    var model: String = "Unknown"
    var batteryLevel: Int = 100

    // 생성자 - 일부 저장 속성을 매개변수로 설정
    init(brand: String, model: String) {
        self.brand = brand
        self.model = model
    }
}

// 인스턴스 생성
let myPhone = Phone(brand: "Samsung", model: "Galaxy S21")
print(myPhone.brand)        // Samsung
print(myPhone.batteryLevel)  // 100 (기본값)

생성자의 오버로딩(Overloading)

  • 오버로딩은 같은 이름의 메서드 또는 생성자를 여러 개 정의하되, 매개변수의 수, 타입, 순서가 다르게 정의하여 사용하는 것이다
  • Swift의 생성자는 오버로딩이 가능하며, 다양한 방식으로 인스턴스를 초기화할 수 있게 해준다

생성자의 오버로딩

class Vehicle {
    var type: String
    var brand: String
    var model: String
    var speed: Double

    // 기본 생성자
    init() {
        self.type = "Unknown"
        self.brand = "Unknown"
        self.model = "Unknown"
        self.speed = 0.0
    }

    // 일부 속성을 초기화하는 생성자 (오버로딩)
    init(type: String, brand: String) {
        self.type = type
        self.brand = brand
        self.model = "Unknown"
        self.speed = 0.0
    }

    // 모든 속성을 초기화하는 생성자 (오버로딩)
    init(type: String, brand: String, model: String, speed: Double) {
        self.type = type
        self.brand = brand
        self.model = model
        self.speed = speed
    }
}

let unknownVehicle = Vehicle()  // 기본 생성자 호출
let bike = Vehicle(type: "Bike", brand: "Yamaha")  // 오버로딩된 생성자 호출
let car = Vehicle(type: "Car", brand: "Tesla", model: "Model S", speed: 250.0)  // 오버로딩된 생성자 호출

기본 생성자(Default Initializer)와 저장 속성의 기본값

기본 생성자(Default Initializer)

  • 기본 생성자는 인스턴스의 모든 저장 속성에 기본값이 설정되어 있을 때 자동으로 제공되는 생성자이다
  • 만약 저장 속성 중 하나라도 기본값이 없으면 기본 생성자가 자동으로 제공되지 않는다

기본 생성자가 자동으로 제공되는 경우

class User {
    var username: String = "Guest"
    var age: Int = 0
}

let user = User()  // 기본 생성자 호출 (자동 제공)
print(user.username)  // Guest
print(user.age)       // 0

기본값이 없는 경우 - 직접 생성자 구현 필요

class Product {
    var name: String
    var price: Double

    init(name: String, price: Double) {  // 직접 생성자를 정의함
        self.name = name
        self.price = price
    }
}

let laptop = Product(name: "MacBook", price: 1299.99)
print(laptop.name)   // MacBook
print(laptop.price)  // 1299.99

구조체의 멤버와이즈 이니셜라이저(Memberwise Initializer)

구조체의 특징

  • 구조체는 클래스와 다르게 멤버와이즈 이니셜라이저를 자동으로 제공한다
  • 구조체의 저장 속성에 기본값이 있으면 기본 생성자도 자동으로 제공된다

구조체의 멤버와이즈 이니셜라이저

struct Book {
    var title: String
    var author: String
    var price: Double = 9.99
}

let book1 = Book(title: "Swift Programming", author: "Apple")
let book2 = Book(title: "Clean Code", author: "Robert Martin", price: 29.99)

구조체의 멤버와이즈 이니셜라이저가 제공되는 이유

  • 구조체의 멤버와이즈 이니셜라이저는 구조체 사용을 더 편리하게 하기 위해 제공되는 것이다
  • 클래스는 상속과 관련된 문제가 있을 수 있기 때문에 자동으로 제공되지 않는다

추가로 알아야 할 사항 (구조체 & 클래스의 차이점)

  1. 클래스는 참조 타입(Reference Type) 이며, 상속이 가능하다
  2. 구조체는 값 타입(Value Type) 이며, 상속이 불가능하다
  3. 구조체는 값 타입이므로 할당이나 전달 시 복사된다
  4. 클래스는 참조 타입이므로 인스턴스를 할당하거나 전달할 때 동일한 인스턴스를 참조한다

구조체 vs 클래스 생성자(Initializer)

구조체의 생성자

  1. 지정 생성자 (Designated Initializer) - init()
  2. 실패 가능 생성자 (Failable Initializer) - init?() 또는 init!()
  3. 멤버와이즈 생성자 (Memberwise Initializer) - 자동으로 제공됨 (구조체 전용)

클래스의 생성자

  1. 지정 생성자 (Designated Initializer) - init()
  2. 편의 생성자 (Convenience Initializer) - convenience init() (상속과 관련)
  3. 필수 생성자 (Required Initializer) - required init() (상속과 관련)
  4. 실패 가능 생성자 (Failable Initializer) - init?() 또는 init!()

구조체의 생성자 (지정 생성자)

지정 생성자 사용

struct Circle {
    var radius: Double
    var color: String
    
    // 기본 생성자 - 기본값을 사용하여 초기화
    init() { 
        self.radius = 1.0
        self.color = "White"
    }
    
    // 지정 생성자 - 반지름과 색상을 모두 초기화
    init(radius: Double, color: String) { 
        self.radius = radius
        self.color = color
    }
    
    // 지정 생성자 - 색상만 초기화하고 반지름은 기본값 사용
    init(color: String) { 
        self.radius = 1.0
        self.color = color
    }
}

지정 생성자 간 호출 (self.init 사용)

struct Rectangle {
    var width: Double
    var height: Double

    // 기본 생성자 - 다른 지정 생성자를 호출하여 초기화 (코드 중복 방지)
    init() {  
        self.init(width: 1.0, height: 1.0)
    }
    
    // 지정 생성자 - 넓이와 높이를 모두 초기화
    init(width: Double, height: Double) { 
        self.width = width
        self.height = height
    }
}

클래스의 생성자 (지정 생성자와 편의 생성자)

클래스의 지정 생성자와 편의 생성자 사용

class Animal {
    var species: String
    var age: Int

    // 지정 생성자 - 모든 저장 속성을 초기화함
    init(species: String, age: Int) { 
        self.species = species
        self.age = age
    }
    
    // 편의 생성자 - 기본값으로 초기화 (편리한 사용)
    convenience init() { 
        self.init(species: "Unknown", age: 0)
    }
}

지정 생성자(Designated Initializer)와 편의 생성자(Convenience Initializer)의 차이점

지정 생성자 (Designated Initializer)

  • 모든 저장 속성을 초기화한다
  • 클래스의 경우, 상위 클래스의 지정 생성자를 반드시 호출해야 한다 (super.init())
  • 지정 생성자는 인스턴스를 완전히 초기화하는 데 필요한 모든 정보를 제공해야 한다
  • 지정 생성자는 동일 클래스 내에서 다른 지정 생성자를 호출할 수 있다
  • 지정 생성자는 편의 생성자를 호출할 수 없다

편의 생성자 (Convenience Initializer)

  • 지정 생성자를 호출하여 초기화를 위임한다 (self.init())
  • 모든 저장 속성을 직접 초기화하지 않아도 된다
  • 편의 생성자는 동일 클래스 내에서 다른 지정 생성자를 호출하여 인스턴스를 생성한다
  • 편의 생성자는 상위 클래스의 지정 생성자를 직접 호출할 수 없다
  • 인스턴스를 간편하게 생성하는 역할을 담당한다

비교 예제: 지정 생성자 vs 편의 생성자

class Vehicle {
    var brand: String
    var speed: Int

    // 지정 생성자 - 모든 속성을 초기화함
    init(brand: String, speed: Int) {  
        self.brand = brand
        self.speed = speed
    }
    
    // 편의 생성자 - 지정 생성자를 호출하여 기본값으로 초기화
    convenience init() {  
        self.init(brand: "Unknown", speed: 0)
    }
}

class Car: Vehicle {
    var model: String

    // 지정 생성자 - 모든 속성을 초기화하고 상위 클래스의 지정 생성자 호출 (델리게이트 업)
    init(brand: String, speed: Int, model: String) {  
        self.model = model
        super.init(brand: brand, speed: speed)
    }
    
    // 편의 생성자 - 일부 값을 기본값으로 설정하여 인스턴스 생성 (델리게이트 어크로스)
    convenience init(model: String) {  
        self.init(brand: "Generic", speed: 120, model: model)
    }
    
    // 편의 생성자 - 모든 값을 기본값으로 초기화함
    convenience init() {  
        self.init(model: "Unknown Model")
    }
}
  • Vehicle 클래스의 convenience init()은 지정 생성자인 init(brand:speed:)를 호출하여 초기화를 위임한다
  • Car 클래스는 Vehicle 클래스를 상속하며, 상위 클래스의 지정 생성자를 호출하기 위해 super.init()을 사용한다
  • 편의 생성자 convenience init(model:)은 동일 클래스 내의 지정 생성자를 호출하여 초기화를 수행한다
  • 편의 생성자는 상위 클래스의 지정 생성자를 직접 호출할 수 없다
  • 상위 클래스의 편의 생성자는 서브클래스에서 자동으로 상속되지 않는다

생성자 위임 규칙 (Initializer Delegation Rules)

델리게이트 업 (Delegate Up)

  • 서브클래스의 지정 생성자는 반드시 상위 클래스의 지정 생성자를 호출하여 초기화해야 한다 (super.init())
  • 상위 클래스의 지정 생성자를 호출하지 않으면 초기화가 완료되지 않는다

델리게이트 어크로스 (Delegate Across)

  • 편의 생성자는 동일 클래스의 다른 지정 생성자를 호출하여야 한다 (self.init())
  • 편의 생성자는 궁극적으로 지정 생성자를 호출하여 초기화를 완료해야 한다
  • 편의 생성자는 상위 클래스의 지정 생성자를 직접 호출할 수 없다
  • 편의 생성자는 상위 클래스에서 상속되지 않는다

생성자 위임 규칙과 상속 관계

class Computer {
    var brand: String
    var ram: Int

    // 지정 생성자 - 모든 저장 속성을 초기화함
    init(brand: String, ram: Int) {  
        self.brand = brand
        self.ram = ram
    }
    
    // 편의 생성자 - 기본값을 사용하여 초기화 (지정 생성자를 호출)
    convenience init() {  
        self.init(brand: "Unknown", ram: 8)
    }
}

class Laptop: Computer {
    var batteryLife: Int

    // 지정 생성자 - 모든 속성을 초기화하고 상위 클래스의 지정 생성자 호출 (델리게이트 업)
    init(brand: String, ram: Int, batteryLife: Int) {  
        self.batteryLife = batteryLife
        super.init(brand: brand, ram: ram)  // 상위 클래스의 지정 생성자를 호출하여 초기화
    }
    
    // 편의 생성자 - 일부 값을 기본값으로 설정하여 인스턴스를 초기화 (델리게이트 어크로스)
    convenience init(batteryLife: Int) {  
        self.init(brand: "Generic", ram: 16, batteryLife: batteryLife)
    }
    
    // 편의 생성자 - 모든 값을 기본값으로 초기화 (편리하게 생성 가능)
    convenience init() {  
        self.init(batteryLife: 10)
    }
}
  • Laptop 클래스는 Computer 클래스를 상속하며, 지정 생성자와 편의 생성자를 모두 가지고 있다
  • Laptop 클래스의 지정 생성자는 super.init()을 사용하여 상위 클래스의 지정 생성자를 호출한다 (델리게이트 업)
  • Laptop 클래스의 편의 생성자 convenience init(batteryLife:)는 동일 클래스의 지정 생성자를 호출하여 초기화를 수행한다 (델리게이트 어크로스)
  • 모든 편의 생성자는 지정 생성자를 반드시 호출해야 한다
  • 편의 생성자는 상위 클래스의 지정 생성자를 직접 호출할 수 없다

2단계 초기화 과정 (클래스의 인스턴스 생성 과정)

1단계 (필수)

  1. 해당 클래스에서 선언한 모든 저장 속성의 값을 초기화한다
  2. 상위 지정 생성자를 호출하여 상위 클래스의 저장 속성들을 초기화한다
  3. 모든 상위 클래스의 저장 속성 초기화가 완료되면 인스턴스는 완전히 초기화된 상태가 된다

2단계 (선택)

  1. 인스턴스 초기화가 완료되면, 모든 생성자가 인스턴스를 추가로 설정할 수 있다
  2. 초기화가 완료된 이후에 self 속성에 접근이 가능해지고, 인스턴스의 메서드를 호출할 수 있다

2단계 초기화 과정 구현 예제

class Appliance {
    var brand: String

    // 지정 생성자 - 모든 저장 속성을 초기화함
    init(brand: String) { 
        self.brand = brand
    }
}

class Refrigerator: Appliance {
    var temperature: Double

    // 지정 생성자 - 모든 저장 속성을 초기화하고 상위 클래스 초기화를 포함함
    init(brand: String, temperature: Double) { 
        self.temperature = temperature  // 1단계: 현재 클래스의 속성 초기화
        super.init(brand: brand)         // 1단계: 상위 클래스의 속성 초기화
    }
    
    // 편의 생성자 - 기본값으로 초기화 (델리게이트 어크로스)
    convenience init() {  
        self.init(brand: "Unknown", temperature: 5.0)
    }
    
    // 인스턴스의 기능을 추가로 설정하는 메서드 (2단계)
    func setTemperature(to newTemperature: Double) {
        self.temperature = newTemperature
    }
}

// 인스턴스 생성 예제
let fridge = Refrigerator()         // 편의 생성자 호출 (초기화 완료됨)
fridge.setTemperature(to: -2.0)     // 2단계: 초기화 후 속성을 변경 가능

let fancyFridge = Refrigerator(brand: "LG", temperature: 4.0) // 지정 생성자 호출
fancyFridge.setTemperature(to: -5.0)

생성자 규칙 요약 및 정리

지정 생성자 (Designated Initializer)

  • 모든 저장 속성을 초기화한다
  • 클래스의 경우 super.init()을 호출하여 상위 클래스의 지정 생성자를 호출해야 한다
  • 지정 생성자는 다른 지정 생성자를 호출할 수 있지만, 편의 생성자를 호출할 수 없다

편의 생성자 (Convenience Initializer)

  • 지정 생성자를 호출하여 초기화를 위임한다 (self.init())
  • 모든 저장 속성을 초기화할 필요는 없다
  • 상위 클래스의 지정 생성자를 직접 호출할 수 없다
  • 지정 생성자 호출을 단순화하고 인스턴스 생성을 더 편리하게 만들어준다

델리게이트 규칙 요약 (Initializer Delegation)

델리게이트 업 (Delegate Up)

  • 서브클래스의 지정 생성자는 반드시 상위 클래스의 지정 생성자를 호출해야 한다 (super.init())

델리게이트 어크로스 (Delegate Across)

  • 편의 생성자는 동일 클래스의 다른 지정 생성자를 호출하여야 한다 (self.init())
  • 편의 생성자는 궁극적으로 지정 생성자를 호출하여 초기화를 완료해야 한다
profile
iOS 개발자 지망생

0개의 댓글