티스토리 뷰

아래의 예제들은 지정생성자(Designated Initializer)와 편의생성자(Convenience Initializer)를 주(main)로 다룬 예제들로, 애플 공식문서의 예제들을 참조하여 코드에 주석을 달아 연습용으로 했던걸 올립니다. 아래의 마무리는 전편의 포스팅을 요약한 것으로 이에 관한 코드예제들입니다.

 

(이니셜라이져(Initializer)를 줄여서 생성자로 불렀습니다.)

5.  (초기화와 생성자 2부의) 마무리

이번단원은 생성자, 넓게보면 구조체와 클래스라는 대단원에서 가장 어렵고 헷갈릴 수 있는 단원이다.

하지만 최초에 정의했던 2가지 법칙만 기억하면된다.

 

현재(Present) 단계에서 선언한 속성 값 초기화(Initialization)

상위클래스의 속성 값은 super키워드를 통해 위임(Delegate)

 

편의생성자(Convenience Initializer)도 결국에는 동일 클래스의 지정생성자를 거치는 보조 생성자에 불과하다.

생성자 위임 규칙(Initializer Delegation)

델리게이트 업(Delegate Up)

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

지정생성자, 편의생성자를 만들때 지켜야하는 당연한 규칙임을 이해하게 된다.

 

클래스의 2단계 재정의중 1단계는 필수사항으로 위에서 다 쓴것을 요약한 것이며

2단계는 선택사항으로, self키워드로의 접근과 커스텀(Customize)이 가능했다는것. 

예외사항인 자동상속 현재 단계의 저장속성값이 셋팅되고, 별도의 지정생성자를 구현하지 않았다면, 

자동상속의 권한을 갖게되며 편의생성자도 자동상속할 수 있다는 것(규칙2)

 

결국에는 저장속성 값이 초기화 되었는지만 꼼꼼하게 체크한다면 

생성자라는 건 (그게 지정이든,편의든, 다음에 등장할 필수든) 매우 쉬워지게 된다.

원래 예제들까지 한번에 묶어 포스팅하려 했으나, 너무 길어져 다음 포스팅은 지정, 편의 생성자에 관한 예제로만 포스팅하고그 다음에 마지막으로 필수생성자와 실패가능생성자에 대해 소개하며 초기화와 생성자에 대한 정리를 마친다.

 

 

예제 1 -  Initializer Delegation (지정생성자와 편의 생성자의 기본적인 사용법)

class Aclass {
    var x = 0

    // init() {}                // 기본 생성자가 자동으로 제공됨
}

let a = Aclass()
print("a 출력해보기 a.x: \(a.x)") //a 출력해보기 a.x: 0

//하위(Sub) B클래스
//상위의 지정생성자 ⭐️ => init()

class Bclass: Aclass {
    var y: Int
    // 상위에 동일한 이름이 있으므로 재정의 해야함 (이 형태는 불가능❗️❗️)
// init() {                // error!
//
//    }

     // (선택 1) 지정생성자로 재정의
    override init() {       // 상위 클래스와 "이름이 동일한 생성자" 구현은 재정의만 가능함❗️❗️
        self.y = 0
        super.init()
    }

//    // (선택 2) 서브클래스에서 편의생성자로 구현해보기
     // 상위 클래스와 "이름이 동일한 생성자" 구현은 재정의만 가능함(단❗️지정생성자 필요)
//    override convenience init() {
//        self.init(y: -1)
//    }
//
//    //선택 1, 선택2의 동시구현은 불가능함 -> 이름이 동일한 생성자라 구분불가❗️
//
//     //(선택 3) 재정의(overriding)하지 않고 현재단계의 생성자 구현
    init(y: Int) {
        self.y = y
        super.init()
    }
}

let b = Bclass() //지정생성자(선택 1)로 재정의시 (선택2는 주석처리)
print("b 출력해보기 b.x: \(b.x), b.y: \(b.y)") //b 출력해보기 b.x: 0, b.y: 0
let b2 = Bclass(y: 30) //재정의 하지 않은 현재단계의 생성자(선택3) 구현시(선택1 혹은 선택2, 아니면 둘다 주석처리하면 된다)
print("b2 출력해보기 b.x: \(b2.x), b.y: \(b2.y)") //b2 출력해보기 b.x: 0, b.y: 30
//아래 let b3은 편의 생성자 구현시를 예시로 든 것.
let b3 = Bclass() //편의 생성자(선택2)로 구현시의 예시(선택 1 주석처리, 선택 2 주석해제)
print("b3 출력해보기 b.x: \(b3.x), b.y: \(b3.y)") //b3 출력해보기 b.x: 0, b.y: -1

//하위(Sub) C클래스
// 상위의 지정생성자 ⭐️ B클래스의 init() init(y: Int)

class Cclass: Bclass {

    var z: Int

    //이 코드는 가능하나 주석처리(아래의 예시값과 다를 것)
//    override init() {      // 1단계 - 상위 클래스와 "이름이 동일한 생성자" 구현(올바른 재정의)
//        self.z = 3
//        super.init()       // 2단계 - 값설정(커스텀)없고, 상위클래스에 기본 init()만 있다면 생략가능
//    }

    override convenience init() {
        self.init(z: 20)
    }

    init(z: Int) {
        self.z = z
        super.init()       // 2단계 값설정(커스텀)없고, 상위구현에 기본 init()만 있는 경우 생략가능
    }
}

var k = Cclass(z: 29) //Cclass단계의 지정생성자
k.x //0
k.z //29
var t = Cclass() //상위클래스를 재정의한 지정생성자/상위클래스를 재정의한 편의 생성자
t.x //0
t.z //20

let c = Cclass()
print("c 출력해보기 c.x: \(c.x), c.y: \(c.y), c.z: \(c.z)") //c 출력해보기 c.x: 0, c.y: 0, c.z: 20

let d = Cclass(z: 1)
print("d 출력해보기 d.x: \(d.x), d.y: \(d.y), d.z: \(d.z)") //d 출력해보기 d.x: 50, d.y: 0, d.z: 1

 

 

예제2(🍎공식문서)Initializer Delegation 및 클래스의 2단계 재정의 규칙

 

class Vehicle {
    
    var numberOfWheels = 0
    var description: String {
        return "\(numberOfWheels) wheel(s)" }
    
    // init() { }
}

// numberOfWheels의 저장 속성에 기본값을 제공, 생성자 구현하지 않았으므로
// ===> 기본 생성자 init() 자동제공

let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")    // Vehicle: 0 wheel(s)

// 상위의 지정생성자 ⭐️ Vehicle의 init()

// 서브클래스 정의

class Bicycle: Vehicle {
    
    override init() {
        super.init()            // 수퍼클래스 호출 반드시 필요
        numberOfWheels = 2      // 초기화의 2단계 값설정❗️
    }
}

// 커스텀 init() 정의 -> 생성자가 상위클래스의 이름과 동일 하므로 재정의 키워드 필요
// 상위 지정생성자 호출하여 메모리 초기화 후, 상위에서 구현한 속성에 접근하여 2로 셋팅(2단계 값설정)

let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")    // Bicycle: 2 wheel(s)

// 상위의 지정생성자 ⭐️
// init()

// 서브클래스 정의
class Hoverboard: Vehicle {
    
    var color: String
    
    // (읽기) 계산 속성 재정의
    override var description: String {
        return "\(super.description) in a beautiful \(color)"
    }
    
//    override convenience init() {
//        self.init(color: "빨간색")
//    }
    override init() {
        self.color = "unknown"
        super.init()
    }
    
    init(color: String) {
        self.color = color      // (현재 클래스) 저장 속성 초기화
        super.init()          
    }
    
}

// 생성자에서 Hoverboard 클래스는 색상 속성만 설정

let hoverboard = Hoverboard(color: "silver")
print("Hoverboard: \(hoverboard.description)")     // Hoverboard: 0 wheel(s) in a beautiful silver

let tay = Hoverboard()
tay.color

 

예제3(🍎공식문서) Initializer Delegation 및 생성자 자동 상속 

// 음식(최상위 클래스)

class Food {
    var name: String
    
    init(name: String) {     // 지정생성자
        self.name = name
    }
    
    convenience init() {     // 편의생성자로 지정생성자 호출
        self.init(name: "[Unnamed]")
    }
}

let namedMeat = Food(name: "Bacon")   // namedMeat의 이름은 "Bacon"
namedMeat.name
let mysteryMeat = Food()      // mysteryMeat의 이름은 "[Unnamed]"
mysteryMeat.name


// 상위의 지정생성자 ⭐️
// init(name: String)    지정생성자
// convenience init()    편의생성자

// 레서피 재료(상위 클래스)

class RecipeIngredient: Food {
    var quantity: Int
    
    init(name: String, quantity: Int) {  // 모든 속성 초기화
        self.quantity = quantity
        super.init(name: name)
    }
    
    override convenience init(name: String) {    // 상위 지정생성자를 편의생성자로 재정의 ===> 지정생성자 호출
        self.init(name: name, quantity: 1)
    }
    
    // convenience init() { }      // 자동 상속 (예외 규칙)
}

//내 레시피(서브 클래스)
class myRecipe: RecipeIngredient{
    var sauce: String
    
    init(sauce:String, name: String, quantity: Int) {
        self.sauce = sauce
        super.init(name: name, quantity: quantity)
    }
    
    override convenience init(name: String, quantity: Int) {
        self.init()
    }
    //같은 파라미터로 받는걸 편의생성자로 구현하러면 override convenience로 하는거ㅎㅎ(설마 헷갈릴까봐ㅠㅠ)
//    convenience init(name: String, quantity: Int) {
//        self.init(sauce: "Spicy", name: name, quantity: quantity)
//    }
    
    //아래의 편의생성자는 연습용으로 여러개 구현

    convenience init(quantity: Int) {
        self.init(sauce: "Sweet", name: "Pizza", quantity: quantity)
    }
    
    convenience init(sauce: String) {
        self.init(sauce: sauce, name: "Pizza", quantity: 5)
    }
    convenience init(quantity: Int, sauce: String) {
        self.init(sauce: "Sweet")
    }
    convenience init() {
        self.init(sauce: "Sweet", name: "Pizza", quantity: 5)
    }
}

let myPizza = myRecipe()

myPizza.sauce //Sweet

let siPizza = myRecipe(name: "CCC", quantity: 5)
siPizza.sauce //Sweet
siPizza.quantity //5
siPizza.name //Pizza

//let myChicken = myRecipe(quantity: 4)
//myChicken.sauce
//myChicken.quantity



let oneMysteryItem = RecipeIngredient()    //  편의생성자
oneMysteryItem.name //[Unnamed]
oneMysteryItem.quantity //1


let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)


// 너무 위일까봐 표기ㅎㅎ
//class RecipeIngredient: Food {
//    var quantity: Int
//
//    init(name: String, quantity: Int) {  // 모든 속성 초기화
//        self.quantity = quantity
//        super.init(name: name)
//    }
//
//    override convenience init(name: String) {    // 상위 지정생성자를 편의생성자로 재정의 ===> 지정생성자 호출
//        self.init(name: name, quantity: 1)
//    }
//
//    // convenience init() { }      // 자동 상속 (예외 규칙)
//}

// RecipeIngredient의 생성자 정리
// init(name: String, quantity: Int)          지정생성자
// override convenience init(name: String)    편의생성자
// convenience init()                         편의생성자

// 쇼핑아이템 리스트(상위 클래스였던 레시피 재료를 상속)

class ShoppingListItem: RecipeIngredient {
    
    var purchased = false       // 모든 저장속성에 기본값 설정
    
    var description: String {
        var output = "\(quantity) x \(name)"
        output += purchased ? " ✔" : " ✘"
        return output
    }
    
    // init(name: String, quantity: Int) {}    // 지정생성자 모두 자동 상속
    // convenience init(name: String) {}       // 따라서 ====> 편의상속자도 모두 자동 상속됨
    // convenience init() {}                   // 따라서 ====> 편의상속자도 모두 자동 상속됨
    
}

var breakfastList = [
    ShoppingListItem(),
    ShoppingListItem(name: "Bacon"),
    ShoppingListItem(name: "Eggs", quantity: 6)
]

breakfastList[0].name
//breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true

for item in breakfastList {
    print(item.description)
}

// 1 x Orange juice ✔
// 1 x Bacon ✘
// 6 x Eggs ✘

출처

Inflearn Allen님의 강의 노트

Swift 공식문서

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함