class Aclass {
var x: Int
required init(x: Int) {
self.x = x
}
}
// 하위 클래스에서도 반드시 필수 생성자를 구현해야 한다.
// 재정의 키워드 override가 필요 없고, required 키워드만 붙여주면 된다.
// 다른 지정 생성자를 구현하지 않으면 자동 상속
class Bclass: Aclass {
// required init(x: Int) { // 자동 상속
// super.init(x: x)
// }
}
// 자동 상속 조건 아닐 때는 반드시 필수생성자 구현해야 한다
class Cclass: Aclass {
init() {
super.init(x: 0)
}
required init(x: Int) {
super.init(x: x) // 호출시 required init으로 호출하지 않음
}
}
struct Animal {
let species: String
// 실패가능 생성자
// (인스턴스를 찍어내는 걸 실패했을 때 nil을 리턴한다
init?(species: String) {
if species.isEmpty { // if species == ""
return nil // 생성자 내에서 실패 가능 부분에 nil을 리턴하면 됨 (문법적 약속)
}
self.species = species
}
}
let a = Animal(species: "Giraffe") // Giraffe
let b = Animal(species: "") // nil
/*활용*/
enum TemperatureUnit {
case kelvin
case celsius
case fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = TemperatureUnit.kelvin
case "C":
self = TemperatureUnit.celsius
case "F":
self = TemperatureUnit.fahrenheit
default:
return nil
}
}
}
// 그냥 생성하는 경우. 실제로는 이렇게 자주 사용
let c: TemperatureUnit = TemperatureUnit.celsius // TemperatureUnit()
// 생성자를 이용해서 생성하는 경우
// nil이 될 수도 있기 때문에 Optional로 선언
let f: TemperatureUnit? = TemperatureUnit(symbol: "F")
// 원시값을 이용하는 경우
enum TemperatureUnit1: Character {
case kelvin = "K"
case celsius = "C"
case fahrenheit = "F"
}
// 원시값이있는 열거형은 자동으로 실패가능 생성자 init?(rawValue :)를 구현함 ==> 일치하면 인스턴스 생성, 아니면 nil
// 원시값이 있는 경우 생성하는 방법. nil을 리턴할 수 있기 때문에 Optional
let f1: TemperatureUnit1? = TemperatureUnit1(rawValue: "F") // .fahrenheit
// 실제로 nil이 리턴되기 때문에
let u: TemperatureUnit1? = TemperatureUnit1(rawValue: "X") // nil
// 실패가능 생성자를 이용해서 구현한 것과 원시값을 이용해서 구현한 것이 거의 유사하다
동일 단계에서 호출 관계 (Delegate across)
struct Item {
var name = ""
/*(1)*/
init() {
}
init? (name: String) { // 실패가능 생성자가 실패 불가능 생성자 호출 -> OK
self.init()
}
/*(2)*/
init() { // 실패 불가능 생성자가 실패가능 생성자 호출 -> NO
self.init(name: "하이")
}
init? (name: String) {
self.name = name
}
상속 단계에서 호출 관계 (Delegate up)
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil } // 상품의 갯수가 1보다 작으면 ====> 카트 항목 생성 실패
self.quantity = quantity // 수량이 한개 이상이면 ====> 초기화 성공
super.init(name: name) // "" (빈문자열이면) ====> 실패 가능 위임 OK
}
}
(상위에서) 실패가능 생성자를 (하위에서) 실패불가능 생성자로 재정의 가능 (강제 언래핑 활용 가능)
(상위에서) 실패불가능 생성자를 (하위에서) 실패가능 생성자로 재정의 불가능
class Document {
var name: String?
init() {} // 실패불가능 생성자
init?(name: String) { // 실패가능 생성자
if name.isEmpty { return nil }
self.name = name
}
}
class AutomaticallyNamedDocument: Document {
override init() { // 재정의 (상위) 실패불가능 =====> (하위) 실패불가능
super.init()
self.name = "[Untitled]"
}
// 실패가능 생성자를 재정의하면서, 내부에서 실패불가능 생성자 활용
// 이런 식으로 실패가 나지 않도록 구현 가능
override init(name: String) { // 재정의 (상위) 실패가능 =====> (하위) 실패불가능
super.init() // 실패불가능 활용가능
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")! // 실패가능 생성자이기 때문에 nil을 리턴할 수도 있다.
// 하지만 문자열을 넣어주고 있기 때문에 nil이 나올 수가 없다.
// 그래서 !를 붙여서 강제 언래핑을 시킨다.
}
}
class Aclass {
var x = 0
var y = 0
deinit { // 실제로 객체가 사라지는지 확인하기 위해 print로 구현
print("인스턴스의 소멸 시점")
}
}
var a: Aclass? = Aclass() // nil을 담을 수 있게 Optional로 선언
a = nil // "인스턴스 소멸 시점" 출력
// nil 할당으로 인해 메모리에 있던 애가 없어지게 되고, 소멸자를 호출하게 된다.
구분 | Struct | Class |
---|---|---|
1. 지정 생성자 Designated | init() {} init(paramter) {} | init() {} init(parameter) {} |
2. 편의 생성자 Convenience | X | convenience init(parameter) {} 상속과 관련 |
3. 필수 생성자 Required | X | required init(parameter) {} 상속과 관련 |
4. 실패가능 생성자 Failable | init?(parameter) {} init!(parameter) {} | init?(parameter) {} init!(parameter) {} |
5. 소멸자 Deinitializers | X | deinit{} |