ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Initializer 2편 (Class의 Initializer)
    오늘의 Swift 상식 2021. 8. 8. 22:48
    728x90
    반응형

     

     

     

     

     

     

     

     


     

     

     

    안녕하세요. iOS 개발자 에이든입니다!👦🏻

     

     

     

    지난 시간 값 타입의 Initializer에 대해서 알아보았죠?

    이어서 바로 Class의 Initializer에 대해 알아보도록 하겠습니다!

     

     

     

    빠르게 들어가 봅시다~

     

     


     

     

    Class의 Initializer에는 다음과 같은 조건이 있어요!

    Class는 본인이 가지고 있는 Property 뿐만 아니라

    부모 Class에서 상속된 Property들도 초기값들을 지정해줘야 합니다! 

     

     

     

     

    훨씬 복잡해지죠? 그래서 Class는 다른 Type에는 없는 Initializer들이 있답니다!


    Class만이 가지는 Initializer

     

    • Designated Initializer
    흔히 보는 기본적인 Initializer에요! 부모 Class의 Initializer를 호출할 수 있어요. Class 내부에는 반드시 한 개 이상의 Designated Initializer가 있어야 합니다!
    init(매개 변수) {
        // 구현부
    }

     

     

    • Convenience Initializer
    Designated Initializer의 일부 매개 변수의 기본값을 설정하여 초기화하는 Initializer에요! 더 적은 입력으로 초기화를 편리하게 할 수 있게 도와주는 역할을 한답니다.
    convenience init(매개 변수) {
        // 구현부
    }

     

     

    예제를 한번 살펴보면 이해가 더 쉬울 거예요!

    class Person {
        var name: String
        var age: Int
      	
        init(name: name, age: age){
            self.name = name
          	self.age = age
        }
      	
        convenience init(name: name){
          	self.init(name: name, age: 27) // Designated Initializer 호출
        }
    }
    
    let aiden = Person(name: "Aiden") // Person(name: "Aiden", age: 29)
    
    // ==========================================================================
    
    // convenience init을 사용하고 싶지 않다면 Property에 초기값을 할당해줘도 됩니다.
    class Person {
        var name: String
        var age: Int = 29
      	
        init(name: name){
          	self.name = name
        }
    }
    
    let aiden = Person(name: "Aiden") // Person(name: "Aiden", age: 29)

     

     

    • Class Initializer의 규칙

    1. Designated Initializer반드시 부모 Class의 Initializer를 호출해야 합니다.

    2. Convenience Initializer반드시 같은 Class의 Initializer를 호출해야 합니다.

    3. Convenience Initializer는 결과적으로 Designated Initializer를 호출해야 합니다.

     

     

     

     

     


    2단계 초기화

    2단계 초기화는 상속한 경우 부모 Class의 Property와 자식 Class의 Proprty를 한 번에 초기화하는 방법이에요.
    class Shape {
        var numberOfSides: Int = 0
        var name: String
    
        init(name: String) {
           self.name = name
        }
    }
    
    class Square: Shape {
        var sideLength: Double = 0.0
    
        init(sideLength: Double, name: String) {
            self.sideLength = sideLength // 1. 자식 Class의 Property에 값 할당
            super.init(name: name) // 부모 Class의 Initializer 호출
            numberOfSides = 4 // 부모 Class가 정의한 Property 값 변경
        }
    }
    🌟주의 사항🌟

    1. 부모 Class를 호출하기 전 반드시 자식 Class의 Property를 초기화해야 합니다!

    2. 부모 Class에 있는 Property에 따로 값을 할당하기 위해선 부모 Class를 먼저 호출해야 합니다. (단, 자식 Class의 Property가 초기화된 경우 자식 Class의 Property에 값을 할당하는 구문은 뒤에 동작해도 됩니다.)

     

     

     

     

     

     

    어떤가요? 값 Type의 Initializer와 많이 다르죠?

    하. 지. 만. 아직 끝이 아닙니다😂

     

    Class가 Struct와 또 다른 점! 바로 상속이죠

    상속 때문에 또 달라지는 Initializer가 있답니다.

    한번 살펴볼까요?


    Initializer 상속

    Swift는 기본적으로 자식 Class에서 부모 Class의 Initializer를 상속하지는 않습니다. 무분별하게 상속이 되면 복잡도가 높아져서 자식 Class를 잘못 초기화하는 상황이 생기기 때문이죠. 그렇기 때문에 특정 조건을 만족할 때에만 Initializer를 상속합니다.
    ✋🏻Initializer를 상속하기 위한 조건

    1. 자식 ClassDesignated Initializer를 정의하지 않은 경우 Designated Initializer를 상속받습니다.

    2. 자식 Class부모 Class의 Designated Initializer를 모두 구현(상속 또는 Override) 한 경우 Convenience Initializer를 상속받습니다.

     

     

     

    그림으로 한번 볼까요?

     

     

     

     

     

    자 그럼 코드로 자세하게 설명드릴게요!

    class Food {
        var name: String
        init(name: String) { // Food Class의 Designated Initializer
            self.name = name // Food Class의 모든 Property를 초기화
        }
        convenience init() { // Food Class의 Convenience Initializer
    	// Designated Initializer를 호출하여 매개 변수에 "[이름 없음]"이란 값 전달
          	self.init(name: "[이름 없음]")
        }
    }
    
    let namedMeat = Food(name: "베이컨") // namedMeat의 name은 “베이컨"
    let mysteryMeat = Food() // mysteryMeat의 name은 “[이름 없음]"
    
    //================================================================
    
    class RecipeIngredient: Food { // Food를 상속받은 Class
        var quantity: Int
        
        init(name: String, quantity: Int) { // RecipeIngredient Class의 Designated Initializer
            self.quantity = quantity
            super.init(name: name) // 부모 Class의 Initializer를 호출하여 name에 값 전달
        }
        // 부모 클래스의 Initializer와 겹치기 때문에 override 키워드를 써서 Override (식별자는 매개변수의 Type과 이름으로 구분하기 때문이죠)
        override convenience init(name: String) { 
            self.init(name: name, quantity: 1)
        }
    }
    // 부모 Class의 모든 Initializer를 구현한 경우, 부모 Class가 가진 Convenience Initializer를 상속 받습니다.
    let oneMysteryItem = RecipeIngredient()
    let oneBacon = RecipeIngredient(name: "베이컨")
    let sixEggs = RecipeIngredient(name: "달걀", quantity: 6)
    
    //================================================================
    
    // RecipeIngredient를 상속 받은 클래스
    // 새로 추가된 모든 Property가 기본 값을 가지고 있으며 별도의 Initializer를 적용하지 않았으므로 부모 Class의 모든 Initializer를 자동으로 상속합니다.
    class ShoppingListItem: RecipeIngredient {
        var purchased = false
        var description: String {
            var output = "\(quantity) x \(name)"
            output += purchased ? " ✔" : " ✘"
            return output
        }
    }
    
    var breakfastList = [
        ShoppingListItem(),
        ShoppingListItem(name: "베이컨"),
        ShoppingListItem(name: "달걀", quantity: 6),
    ]
    
    // 참고: "[이름 없음]"을 "오렌지 쥬스"로 변경 및 구매한 것으로 변경
    breakfastList[0].name = "오렌지 쥬스" 
    breakfastList[0].purchased = true
    for item in breakfastList {
        print(item.description)
    }
    // 1 x 오렌지 쥬스 ✔
    // 1 x 베이컨 ✘
    // 6 x 달걀 ✘

     

     

    • Required Initializer
    Class에서 반드시 구현해야 하는 Initializer에요. 앞에 required라는 키워드를 붙여 사용 가능하고, 해당 Class를 상속받은 Class에서 구현할 때에도 required 키워드를 붙여야 합니다! 참고로 required는 Override를 기본으로 포함하고 있어요!
    class SomeClass {
        required init() {
            // 구현부
        }
    }
    
    class SomeSubclass: SomeClass {
        required init() {
            // 자식 Class의 Required Initializer 구현
        }
    }

     

     

     

     

     

    마지막으로 특이한 Initializer 하나 보여드리고 마무리할게요!

    Initializer는 Closure를 활용할 수도 있어요!


    Closure를 활용한 Initializer

    초기화 과정이 단순히 값을 할당하는 것이 아닌 복잡한 계산을 필요로 한다면 Closure나 함수를 이용해 값을 초기화할 수 있어요!
    ✨깨알 Tip✨

    1. Closure가 끝난 뒤에 괄호'()'를 붙여 Property가 Closure 자체가 아닌 연산의 결과를 가지도록 할 수 있습니다.

    2. 연산 Property와 다른 점은 연산 Property는 변수를 참조할 때마다 호출되지만 Closure를 활용한 Initializer는 한 번만 호출됩니다.
    class SomeClass {
        let someProperty: SomeType = {
            // 이 클로저의 코드로 someProperty를 위한 기본 값을 생성합니다.
            return someValue // someValue는 SomeType과 반드시 같은 Type!
        }()
    }

     

     

     


     

     

    이틀에 걸쳐 Initializer에 대해 공부해보았습니다!

    파면 팔수록 계속 뭐가 나와서 순서가 조금 엉망일 순 있어요ㅎㅎ;;

    Initializer는 필수로 쓰이는 만큼 이 글이 많은 도움이 되었으면 좋겠네요

    자 그럼 Initializer 편은 여기서 마치도록 하겠습니다!

     

     

    혹시라도 부족하거나 잘못된 부분 그리고 질문 있으시면 언제든 댓글 부탁드려요! 감사합니다!👦🏻👋🏻

     

    728x90
    반응형

    '오늘의 Swift 상식' 카테고리의 다른 글

    Protocol 1편 (Protocol 정의 방법)  (0) 2021.08.16
    Class의 상속  (0) 2021.08.15
    Initializer 1편 (Struct의 Initializer)  (0) 2021.08.08
    Struct, Class  (2) 2021.07.18
    iOS 앱 생명주기  (3) 2021.07.14

    댓글

Designed by Tistory.