기본 생성자를 제외하고도 많은 종류의 생성자가 있다는 걸이번 부트스 캠프를 통해 알게되었다.
다양한 생성자의 역할과 특징을 알아보자.
1. Designated initializers
첫 번째는 지정 생성자이다.
역할
가장 기본적인 생성자이며 초기화 되지 않은 모든 프로퍼티를 초기화 해주는 역할을 한다.
특징
1) Delegate Up
서브 클래스의 Designated Initializer는 반드시 슈퍼 클래스의 Designated Initializer를 호출해야 합니다
2. convenience initializers
두번째는 편의 생성자이다.
역할
Designated init의 파라미터 중 일부를 기본값으로 설정해서, convenience init안에서 Designated init을 호출하는
보조 생성자 역할을 한다.
특징
1) Delegate Across
- Convenience Initializer는 반드시 동일한 계층(클래스)의 initailizer를 호출해야 한다.
- Convenience Initializer는 최종적으로 동일한 계층(클래스)의 Designated initialize를 호출
3. memberwise init
구조체가 자동으로 제공하는 생성자
역할
구조체 내에 Designated initializers가 구현되지 않을 때 Designated initializers의 역할을 하는 생성자 역할을 한다.
특징
1) 생성자의 파라미터는, 프로퍼티가 선언된 순서, 갯수, 변수명이 모두 일치하게 제공된다.
2) 이미 let으로 선언하거나 초기화가 진행된 변수는 파라미터에서 제외된다.
3) 프로퍼티 중 하나라도 private으로 설정되어 있다면 memberwise init 역시 private으로 제공되어 외부에서 사용이
불가능하다.
4) Designated initializers을 정의되면 더이상 제공되지 않는다.
5) extension에 Designated initializers을 정의하면 계속하여 사용할 수 있다.
예시 코드
struct StructA {
var prop1: Int
var prop2: Int
let const1: Int = 10
}
extension StructA {
init(p1: Int, p2: Int) {
self.prop1 = p1
self.prop2 = p2
}
}
var sa1 = StructA(prop1: 10, prop2: 20) // ✅ memberwise 생성자 이용 , const1은 제외됨
var sa2 = StructA(p1: 10, p2: 20) // ✅ extension에 있는 Designated 생성자 이용
4. required init
필수 생성자
역할
상속 또는 프로토콜을 채택할 때 사용하는 생성자
특징
1) 상속 시
- 슈퍼 클래스에서 정의해둘 경우 서브 클래스가 슈퍼 클래스의 생성자를 상속받지 않는 한 반드시 구현 해야하는 생성자
- override 키워드 없이, 슈퍼 클래스와 동일한 형태로 구현, 결과적으로 override는 맞음
2) 프로토콜 채택 시
- 프로토콜을 채택한 타입은 Initializer은 designated initializer나 convenience initializer로 구현할 수 있고 모두 required 키워드를 붙여야 한다.
- 만약 채택하는 class 가 final 일 경우 required 가 필요없다.
3) init이 있는 프로토콜 + 동일한 init를 갖는 클래스를 동시에 상속
- 위 두가지 생성자의 파라미터가 같을 경우 required override 붙혀야한다.
예시 코드
1) 상속 시
class Human {
var name: String
required init(name: String) {
self.name = name
}
}
class Developer : Human {
var nickName: String
init(nickName: String) {
self.nickName = nickName
super.init(name: "H")
}
required init(name: String) {
self.nickName = "Nick_\(name)"
super.init(name: name)
}
}
var dev1 = Developer(name: "requiredName") // required init 이용
var dev2 = Developer(nickName: "Nick") // designated init
print(dev1.nickName, dev1.name) // Nick_requiredName requiredName
print(dev2.nickName, dev2.name) // Nick H
2) 프로토콜 채택 시
protocol P {
init (name: String)
}
class Human: P {
var name: String
required init(name: String) {
self.name = name
}
}
final class FinalHuman: P {
var name: String
init(name: String) {
self.name = name
}
}
3) init이 있는 프로토콜 + 동일한 init를 갖는 클래스를 동시에 상속
protocol P {
init(param: Int)
}
class Junior {
var param: Int
init(param: Int) {
self.param = param
}
}
class Student: Junior, P {
var studentParam: Int
init(studentParam: Int) {
self.studentParam = studentParam
super.init(param: studentParam)
}
required override init(param: Int) {
self.studentParam = param
super.init(param: param)
}
}
5. Failable Initializer
실패 가능한 생성자
역할
생성자 함수 안에 error 발생 가능한 코드가 있을 시 사용 , 만약 실패한다면 호출한 부분이 nil 리턴
특징
1) init? 인 경우 , 모든 형태로 사용을 할 수 있다
1) init 인 경우 , init? 형태로 사용할 수 없다.
예시 코드
protocol P1 {
init?(name: String)
}
protocol P2 {
init(name: String)
}
struct StructA: P1 {
init?(name: String) { // ✅ 채택 가능
}
init(name: String) { // ✅ 채택 가능
}
init!(name: String) { // ✅ 채택 가능
}
}
struct StructB: P2 {
init?(name: String) { // ❌ 불가
}
init(name: String) { // ✅ 가능
}
init!(name: String) { // ✅ 가능
}
}
소감
기본 생성자를 제외하고 써본 경험이 거의 없는데 이번 계기를 통해 상황에 맞는 생성자를 사용해 볼 수 있을 것 같다.
'프로그래밍언어 > swift' 카테고리의 다른 글
protocol 학습하기 (4) [ any, some ] (0) | 2024.08.25 |
---|---|
protocol 학습하기 (3) [ associatedtype ] (0) | 2024.08.25 |
protocol 학습하기 (2) [ generic, 합성, 채택 체크 ] (0) | 2024.08.25 |
protocol 학습하기 (1) [ property, method, extension ] (0) | 2024.08.24 |
class와 struct (0) | 2024.08.24 |