본문 바로가기

Swift/Deep dive!!

[Swift] 자동, 지정, 편의 초기화(in class). 연쇄 호출 관계 뿌수기!!

 

 



클래스와 초기화에 대한 개념이 가물~가물 하다면?! -> 클래스와 초기화 글 다시보기


About Designated Initializer(지정 초기화)

 

지정 초기화는 기본 초기화입니다.
옵셔널 타입이 아닌 저장 프로퍼티를 전부 초기화 시켜 주어야 한다. else error
( 또한 super클래스의 초기화 메서드도 호출 해주어서 초기화 체인을 이룰 수 있도록 하는 초기화입니다)

init(parameters) {
    statements
}

따라서 클래스는 반드시 한개 이상의 초기화 Designated Initializer를 가지고 있어야 합니다.(갖고만 있으면 안돼.. 저장 프로퍼티 값들 전부 할당해야 함)

 

이때의 지정 초기화 메서드는 또한 부모 클래스의 초기화 메서드를 호출 해야 한다.

 

... 초기화 메소드의 델리게이션 ... 

 

자식의 지정 초기자에서 super.init()을 호출했는데 또 조부모가 있다면? ....

따라서 지정 초기화 메서드는 부모 클래스의 초기화 메서드를 호출해야 합니다.


Automatic Initializer Inheritance(자동 초기화 메서드 상속)

 

자자!! 

 

자식클래스에서 초기화 지정할 경우에는 부모 클래스의 초기화 메서드 호출 안시켜줍니다.

이와 반면에 자식클래스에서 지정 초기화 메소드를 구현하지 않으면  ( override된 초기화 메서드 사용 x) 자동으로 부모 클래스의 지정 초기화 메소드가 상속 된다.

 

  Rule 1

자식 클래스가 아무런 지정 초기화를 정의하거나 오버라이딩 하지 않을 경우
자동적으로 모~든 부모클래스의 지정된 생성자를 상속 받습니다.

  Rule 2

자식 클래스가 부모 클래스의 지정 초기화 메서드를 전부 구현하거나,
Rule 1의 규칙에 의해 모두 상속 받을 경우 
자동적으로 모든 부모 클래스의 convenience initizlizers(편의 초기화)를 추가합니다.

특히 Rule 2 를 만족시킬 경우

자식 클래스는 부모 클래스의 지정 초기화 메소드 자식 클래스coinvenience initializers(편의 초기화)로써 구현이 가능합니다. 
( 추가를 할 수있습니다. )

 

Rule2를 간단하게 생각할 수도있는데 어마무시합니다..

 

자식클래스에서 Designated 메소드는 구현하지 않고 Convenience 이니셜라이저만 구성했을때는

자동 초기화 메서드 상속이 일어날까요??

 

눼!! 일어납니다... ㅎㄷㄷ

자식클래스에서Designated 이니셜라이저를 구현 하지 않는다면 자동으로 부모의 Designated 이니셜 라이저를 상속받습니다.

따라서 Convenience를 구현한 자식 메서드는 부모의 지정 초기자를 self.init( ... ) 로 호출해야합니다.


About Convenience Initializer(편의 초기화)

 

편의 초기자는 내부적으로 다른 초기화 메서드 ( type : Convenice or Designated)를 호출한다.

Convenienve 초기화 간 연쇄 호출이 일어날 수 있다.

하지만

연쇄 호출의 끝인 편의 초기자는 반드시 지정 초기자를 호출해야 한다.

 

Designated 초기자와 구별되기 위해 convenience 키워드가 붙는다.

 

미리 지정된 값을 사용해서 input 되도록 없이 최소한의 입력으로 초기화 해줄 수 있도록 해준다!!!

convenience init(parameters) {
    statements
}

 

//아래 사진은 Convenience 초기화의 동작 원리를 보여줍니다~~

  Food클래스에서 Designated와 Convenience의 관계

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
    convenience init() {
        self.init(name: " haha : ) ")
        print("yeah~~")
    }
}
  • Designated init(name:) 호출 경우
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"
  • Convenience init() 호출 경우
let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"

// 아래 사진은 Food 클래스 타입의 변수가 초기화 될 때 초기화 함수가 호출되는 동작을 보여줍니다!!

  Food클래스를 상속받은 RecipeIngredient의 초기화 관계

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)
    }
}

Food의 convenience를 오버라이딩 해도 결국 convenience 타입이기 때문에 자신의 지정 초기화 메서드 self.init(name:quality:)를 호출해야 합니다.

호출 규칙에 의해 RecipeIngredient Desiganted 초기화 메서드는 결국 super의 Designated 초기자를 호출하며 호출 규칙을 만족시킵니다.(+ 부모 저장 프로퍼티 알아서 초기화)

 

  RecipeIngredient클래스를 상속받은 ShoppingListItem의 초기화 관계

class ShoppingListItem: RecipeIngredient {
    var purchased = false
    var description: String {
        var output = "\(quantity) x \(name)"
        output += purchased ? " ✔" : " ✘"
        return output
    }
}

여기서는 Designated init을 사용하지 않았으므로 아무런 초기화 메서드가 없습니다. 하지만 새롭게 정의된 자신의 두 저장 프로퍼티는 생성과 동시에 초기화를 했으므로, 이 경우에는 부모 초기화의 호출 관계를 자동으로(위에서 설명한 자동 초기화 메서드 상속 규칙에 의해) 물려 받습니다.

-> 상당히 편리함!!

이 그림의 뜻은 ShoppingListItem에서는 초기화를 선언하지 않았지만 , 자동으로 초기화 메서드 상속 규칙에 의해 부모 초기화 메서드 사용하면서 인스턴스를 생성해도!!! 이미 자신의 저장 프로퍼티는 초기화 했기 때문에 문제없이 ShoppingListItem타입의 변수, 상수를 생성할 수 있다는 것입니다!!

 

호출 규칙(지정초기자와 편의 초기자의 관계)

 

지정 초기자와 편의 초기자 메소드 사이의 호출 규칙

  • 호출 규칙
  1.  지정 초기화 메소드는 클래스 내 모든 저장 프로퍼티에 할당 한 후, 부모의 지정 초기화 메소드를 호출 해야함.
  2.  편의 초기화 메소드는 같은 클래스 내 정의된 다른 지정 초기화 메소드 반드시 호출
  3. 편의 초기화 연쇄 호출은 최종적으로 정의된 지정 초기화 메서드를 호출해야 한다.

편의 초기화의 경우 같은 클래스 내 정의된 다른 초기화 메소드를 호출한다. (연쇄 호출로 초기화)
근데 이때 연쇄 호출된 초기화가 편의 초기화일 경우.. 마지막으로 호출된 편의 초기화는 반드시 지정 초기화를 호출해야한다.

super 클래스의 맨 오른쪽 Convenience init화살표 보면 연쇄적으로 Convenience init을 호출하는데 결국엔 Desiganted init을 호출한다.

 

이 superClass의 조상클래스가 있다면?! Desiganted가 호출됨으로써 super Designated를 호출시켜야 한다. ( 위 규칙 1에 의해) 따라서 지정 초기자와 편의 초기자 메소드 사이에는 호출 규칙들이 지켜져야 한다.

 

여러 개의 클래스와 상속 관계가 있는데 결국 Convenience들은 Designated를 호출하고, 해당 클래스의 Designated는 super Designated 초기화를 호출한다는 뜻!

 

요기서 Designated는 계층적으로 연쇄 초기화를 이룬다!! ( 자식 클래스에서 모든 저장 프로퍼티를 초기화 해야하는 불편한 수고를 덜어줌)

 

오호!!

  원리 1

지정 초기화 메소드는 항상 상위 방향(super Designated)으로 호출한다.

  원리 2

편의 초기화 메소드는 항상 수평 방향(current Designated or Convenience)으로 호출한다.