구조체 생성자
- 오버로딩(같은 이름인데, parameter를 다르게 함)을 이용해서 여러 가지 생성자를 만들 수 있지만, 올바른 구현 방법은 아니다
지정 생성자
- 값 타입 (value type) (구조체)의 경우, 자체 지정생성자를 작성할 때,
생성자 내에서 self.init을 이용해서 다른 생성자 호출할 수 있다.
- 구조체는 다른 생성자를 호출하는 방식이 가능하다!
- 코드가 중복되지 않기 때문에 이 방법이 더 올바르다.
struct Color1 {
let red, green, blue: Double
init() {
self.init(red: 0.0, green: 0.0, blue: 0.0)
}
init(white: Double) {
self.init(red: white, green: white, blue: white)
}
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
}
클래스 생성자
지정(designated) 생성자 vs 편의(convenience) 생성자
- 지정생성자 : 일반적인 initializer (init)
- 편의생성자 : 다른 생성자를 호출하는 initializer
- 구조체에서는 편의생성자라는 개념이 없다.
class Color2 {
let red, green, blue: Double
convenience init() {
self.init(red: 0.0, green: 0.0, blue: 0.0)
}
convenience init(white: Double) {
self.init(red: white, green: white, blue: white)
}
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
}
var c = Color2(
- 지정 생성자는 모든 속성을 초기화해야 한다 (당연)
- 편의 생성자는 모든 속성을 초기화 할 필요가 없다 (지정 생성자에 의존)
- 클래스는 상속을 지원하기 때문에
- 여러 지정 생성자를 지원하면 상속 관계에서 개발자의 실수 가능성이 있다
- 따라서, 초기화 과정을 조금 간편하게 하고, 실수 여지를 줄이기 위한 생성자이다
- (이미 모든 속성을 초기화하는 지정생성자가 있으면)
모든 속성을 초기화 하지 않으면 -> 편의생성자로 만드는 것이 낫다 (복잡도, 실수)
- 결국, 생성자의 가능한 중복을 없애고, 다른 지정 생성자를 호출하는 패턴으로 구현해야 한다
상속 관계에서의 예시
class Aclass {
var x: Int
var y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
convenience init() {
self.init(x: 0, y: 0)
}
}
class Bclass: Aclass {
var z: Int
init(x: Int, y: Int, z: Int) {
self.z = z
super.init(x: x, y: y)
}
convenience init(z: Int) {
self.init(x: 0, y: 0, z: z)
}
convenience init() {
self.init(z: 0)
}
func doSomething() {
print("Do something")
}
}
let a = Aclass(x: 1, y: 1)
let a1 = Aclass()
let b = Bclass(x: 1, y: 1, z: 1)
let b1 = Bclass(z: 2)
let b2 = Bclass()
- 지정생성자는 반드시 재정의
- 반드시 새로운 저장속성 초기화 (
self.z = z
)
- 반드시 상위 지정생성자 호출 (
super.init(x: x, y: y
)
- 편의생성자는 상속이 되지 않는다
호출 규칙
- Delegate up
- 지정생성자가 상위의 지정생성자 호출하는거
- sub-class의 지정생성자는 super-class의 지정생성자를 반드시 호출해야 한다
- 하위 클래스의 저장속성을 초기화(
self.z = z
)하고,
상위클래스의 저장속성을 초기화(super.init(x: x, y: y
)
- 하위의 메모리를 먼저 찍어내고, 상위의 메모리를 찍어낸다 (??)
- Delegate across
- 편의 생성자가 호출하는거
- 편의생성자는 동일한 class에서 다른 initializer를 호출해야 하고,
궁극적으로 지정생성자를 호출해야 한다
- 결국, 지정생성자만이 해당 단계의 모든 저장값을 초기화
지정생성자 / 편의생성자 상속과 재정의 규칙
- 생성자는 기본적으로 상속되지 않고 재정의 원칙
- 재정의 : 동일한 이름을 가진 생성자를 구현하는 것
- 상위 지정생성자와 현재 단계의 저장 속성을 고려하기
- 상위 지정생성자
- (상위) 지정생성자 : 재정의 필수 고려 (1. 지정, 2. 편의, 3. 안한다(??))
- (상위) 편의생성자 : 재정의 불가 (호출 불가)
- 현재 단계의 저장 속성
- (지정 생성자 내에서) 모든 저장 속성 초기화 및 상위 지정 생성자 호출
- (편의생성자 내에서) 현재 단계의 지정생성자 호출
class Aclass {
var x = 0
}
let a = Aclass()
class Bclass: Aclass {
var y: Int
override init() {
self.y = 0
super.init()
}
init(y: Int) {
self.y = y
super.init()
}
}
let b = Bclass()
class Cclass: Bclass {
var z: Int
override init() {
self.z = 0
super.init()
}
override init(y: Int) {
self.z = 0
super.init(y: y)
}
init(z: Int) {
self.z = z
super.init()
}
}
let c = Cclass()
let d = Cclass(z: 1)
Review (공식 문서 예제)
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")
class Hoverboard: Vehicle {
var color: String
override var description: String {
return "\(super.description) in a beautiful \(color)"
}
override init() {
self.color = "빨간색"
super.init()
}
override convenience init() {
self.init(color: "빨간색")
}
init(color: String) {
self.color = color
super.init()
}
}
let hoverboard = Hoverboard(color: "silver")
print("Hoverboard: \(hoverboard.description)")
예외사항 : 자동 상속
- 지금까지 배운거
- 생성자 : 기본적으로 상속되지 않고, 재정의가 원칙이다
- 그 중 지정생성자만 재정의하고, 편의생성자는 재정의가 불가하다
- 지정생성자 자동 상속
- 저장속성의 기본값 설정 및 어떤 재정의도 하지 않았을 때
(새 저장속성이 아예 없거나,
또는 새 저장속성이 있는데 이미 기본값 설정한 경우)
- 편의생성자 자동 상속
- 상위 지정생성자를 모두 상속
- 지정생성자 모두 자동상속(위에 있는거) or 지정생성자 모두 재정의
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
let mysteryMeat = Food()
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)
}
}
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}
var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: "Bacon"),
ShoppingListItem(name: "Eggs", quantity: 6),
]