Swift

Swift ) Designated Initializer, Convenience Initializer

JoonSwift 2022. 5. 2. 16:37

Designated Initializer?

designated initializer는 클래스의 기본적인 이니셜라이저입니다. 보통 클래스에서 이니셜라이저를 생성하면 작성하게 되는

init(parameters) {
  statements
}

이런 형태를 가진 이니셜라이저를 designated initializer라고 합니다. 클래스는 상속이 가능하기에, 이 designated initializer는 정말 중요한 역할을 합니다. 바로 상위 클래스(super class)의 이니셜라이저를 delegate하는 역할을 수행할 때 사용하기 때문입니다. 

class User {
  let email: String
  let name: String
  let authority: String
  
  init(email: String, name: String, authority: String) {
    self.email = email
    self.name = name
    self.authority = authority
  }
}

class SpecialUser: User {
  init(greeting: String) {
    super.init(email: "special@email.com", name: "special", authority: "user")
    print(greeting + "I'm \(name)")
  }
}

let speicalUser = SpecialUser(greeting: "Hi!") // Hi!I'm special

SpecialUser 클래스는 User클래스의 subclass입니다. 그래서 이니셜라이저 코드에 super.init( ... ) 을 추가하여 해당 클래스의 superclass 즉, User 클래스의 이니셜라이저를 호출합니다. 

 

UIKit에서 대표적으로 정말 많이 보는 viewDidLoad(animated: )가 있습니다. 항상 viewDidLoad 메서드를 override할 때, super.viewDidLoad를 호출해왔는데, 이는 UIViewController 클래스에 선언되어있는, viewDidLoad 실행문을 실행시킨다는 의미와 같습니다. 

Designated Initializer의 Initializer Delegation 형태는 위의 그림과 같습니다. 이는 Swift 공식문서의 'Initializer Delegation for Class Types' 파트에서 Initializer들 간의 관계에 대한 Rule을 정리해놓은 부분이 있는데,

Rule 1
A designated initializer must call a designated initializer from its immediate superclass.

해당 Rule에 해당하는 케이스라고 볼 수 있습니다. 어떤 클래스가 super class가 있다면, 그 super class의 designated initializer를 꼭 호출해주어야 한다는 룰입니다. (super class는 본인이 super class이므로 해당 룰에 적용되지 않습니다.)

 

Convenience Initializer

Convenience Initializer는 supporting initializer라고도 합니다. Convenience Initializer의 주된 역할은, 같은 클래스에 있는 Designated Initializer의 파라미터에 기본값(Default Value)를 주는 역할을 하거나 그 클래스의 인스턴스를 생성하여 특별한 용도로 사용하기도 합니다. 

convenience init(parameters) {
  statement
}

init 앞에 convenience 를 붙여 사용합니다. 

아까 위의 예제에서 init 아래에 convenience init을 추가해보도록 하겠습니다. 

// In User class

  // init 
  
  convenience init(name: String) {
    var email: String = ""
    var authority: String = ""
    
    if name == "admin" {
      email = "admin@email.com"
      authority = "Admin"
    } else {
      fatalError("관리자가 아니면 name만으로 생성할 수 없습니다.")
    }
    
    self.init(email: email, name: name, authority: authority)
  }

활용법은 다음과 같습니다. convenience initializer 내부에는 name이 admin인지 확인하여 email 과 authority에 값을 넣어 self.init을 호출해주는 코드입니다.

let user = User(email: "user@email.com", name: "user", authority: "user")
let admin = User(name: "admin")
let notAdmin = User(name: "notadmin") // Fatal Error!

여기서 admin 과 notAdmin은 convenience initializer를 통해 생성된 인스턴스입니다. notAdmin의 경우 fatalError 코드가 호출되는데 이는 convenience initializer에서 name이 "admin"이 아니라면 fatalError 코드를 실행하게 했기 때문입니다. 

print(user.email) // user@email.com
print(admin.email) // admin@email.com

Convenience Initializer는 옆으로(?) 가는 형태로 볼 수 있습니다. Swift.org에 나머지 Rule 2, 3에 대한 것도 확인해보겠습니다. 

Rule 2
A convenience initializer must call another initializer from the same class.
Rule 3
A convenience initializer must ultimately call a designated initializer.

Convenience initializer는 반드시 같은 클래스에 있는 다른 initializer를 호출해야 합니다. 이 다른 initializer는 Designated Initializer가 될 수도, Convenience Initializer가 될 수도 있습니다. 하지만 중요한건 Same class! 

Rule 3는 Convenience initializer는 반드시 마지막에는 Designated initializer를 호출해야 합니다. 즉, 옆으로 옆으로 Convenience initializer들을 타고 가다가도 마지막 화살표는 해당 클래스의 Designated Initializer를 향해야 한다는 의미입니다. 

 

 

정리

처음에 영어로 Designated Initializer라는 표현이 처음 들어보는 표현이라 어색했었습니다. 항상 그냥 이니셜라이저라고 부르던 녀석이 Designated initializer라는 이름을 가지고 있었다니.. 

Convenience initializer에 대해서도 알아가는 시간을 가졌습니다. 코드를 작성하면서 한번도 써보지 않아 어떤 역할을 하는 녀석인지 몰랐지만, 이번 포스팅을 통해 알게되었습니다.

또한 Initializer Delegation이라는 표현과 Rule이 있다는 사실 또한 배워가는 좋은 포스팅이 되었습니다~

 

참고 문서

https://docs.swift.org/swift-book/LanguageGuide/Initialization.html

 

Initialization — The Swift Programming Language (Swift 5.6)

Initialization Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization t

docs.swift.org