-
swift) Initializationswift 2021. 6. 19. 16:29
역시 기초가 제일 중요하고, 어려운 것 같아요 😂오늘은 swift의 initialization에 대해 알아봅시다.
Two-Phase Initialization
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID220
Swift에서 클래스는 2단계로 초기화 됩니다. 첫 번째는 각 저장 프로퍼티들이 그 해당 클래스에 의해 초기 값을 할당 받아요. 모든 저장 프로퍼티의 초기 값이 결정되면, 두 번째 단계가 시작됩니다. 각 클래스는 새로운 인스턴스의 사용 준비가 끝나기 전에 각 저장 프로퍼티를 커스터마이즈 할 기회를 가집니다.
2단계의 초기화 과정은 클래스 계층에서의 각 클래스의 완전한 유연함을 제공하면서 안전하게 초기화 할 수 있도록 해줍니다. 또한 프로퍼티 값이 초기화되기 전에 접근하는 것을 막아주고, 다른 예기치 않은 이니셜라이저에 의해 다른 값으로 설정되는 것을 방지합니다.
Swift의 two-phase initialization은 Objective-C에서의 초기화와 비슷합니다. 가장 큰 차이점은 첫 번째 단계에서 Objective-C는 모든 프로퍼티에 0이나 nil 값을 할당하지만, Swift는 사용자 지정 초기 값을 지정할 수 있어 유연하고 0이나 nil이 기본 값으로 적합하지 않는 타입들을 다룰 수 있어요.
Swift 컴파일러는 two-phase initialization이 에러없이 완료되는지 확인하기 위해 4개의 safety-checks를 수행합니다.
Safety check1
지정된 이니셜라이저는 모든 프로퍼티가 superclass 이니셜라이저까지 위임되기 전에 초기화 되어야 한다.
객체의 메모리는 모든 프로퍼티의 초기 상태가 있어야만 완전히 초기화 된 걸로 간주됩니다. 이 조건을 충족시키기 위해서 현재 이니셜라이저가 체인을 넘겨주기 전에 (superClass의 initializer를 호출하기 전에) 모든 프로퍼티의 초기화를 진행해야 합니다.
class SuperClass { var name: String var age: Int init(name: String, age: Int) { self.name = name self.age = age } } class SubClass: SuperClass { var type: String init(type: String) { super.init(name: "hi", age: 13) self.type = type // error } }
다음과 같이 SubClass의 self.type이 초기화 되지 않은 상태에서 super class가 생성자를 호출할 수 없어요.
Safety check2
지정된 이니셜라이저는 상속된 프로퍼티에 값을 할당하기 전에 super class의 이니셜라이즈를 호출해야한다. 그렇지 않으면 지정된 이니셜라이저가 할당한 새 값은 super class의 이니셜 라이저에서 다시 쓰여질 것이다.(overwritten)
class SubClass: SuperClass { var type: String init(type: String) { self.type = type self.age = 11 // error super.init(name: "hi", age: 13) } }
Safety check3
convenience 이니셜라이저는 어떤 프로퍼티(같은 클래스에 의해 정의된 프로퍼티 포함)에 값을 할당하기 전에 다른 이니셜라이저를 호출해야한다. 그렇지 않으면 convenience 이니셜라이저가 할당한 새 값은 지정된 이니셜라이저에 의해 다시 쓰여질 것이다.
convenience init(age: Int) { self.name = "welly" // error self.init(name: "welly", age: age) }
Safety check4
이니셜라이저는 첫 번째 초기화가 완료되기 전까지 어떠한 인스턴스 메서드 호출, 인스턴스 프로퍼티 값 읽기, self 참조 등을 할 수 없다.
class Student { var age: Int init() { self.study() // age가 초기화되지 않은 상태이므로 self로 접근 불가! self.age = 17 } func study() { print("studying... ") } }
클래스 인스턴스는 첫 번째 초기화가 끝나기 전까지는 유효하지 않은 상태입니다. 첫 번째 초기화가 끝나고 유효한 상태가 되어야지만 프로퍼티에 접근하거나 메소드를 호출할 수 있어요.
위 4 가지 규칙들에 의한 two-phase 초기화 방법
[Phase 1]
- 지정 이니셜라이저 (그냥 init)이나 convenience 이니셜라이저가 class 에서 호출
- 해당 클래스의 새로운 인스턴스를 위한 메모리 할당. 메모리는 아직 초기화 되지 않은 상태
- 지정 이니셜라이저는 모든 저장 프로퍼티가 해당 클래스에 의해 초기 값을 가지고 있는지 확인. 저장 프로퍼티에 대한 메모리가 현재 초기화 됨
- 지정 이니셜라이저는 super class의 이니셜라이저를 호출하여 저장 프로퍼티에 대해 같은 작업을 수행
- 체인의 맨 위에 도달할 때까지 클래스 상속 체인을 거슬러 올라감
- 가장 상위에 도달하여 마지막 class가 모든 저장 프로퍼티가 값을 가짐을 보장하면, 인스턴스의 메모리가 완전히 초기화되었다고 간주하고 phase 1 초기화 종료
[Phase 2]
- 체인의 상단에서부터 다시 내려오면서 각 지정 이니셜라이저는 인스턴스를 추가로 커스터마이징 할 수 있음. 이니셜라이저는 self에 접근 가능하고 프로퍼티 수정, 인스턴스 메소드 호출 등의 작업이 가능
- 마지막으로, 체인의 어떠한 convenience 이니셜라이저도 인스턴스를 커스터마이징 가능하고 self를 호출 가능
예를 들어, 위의 서브클래스의 convenience 이니셜라이저 호출로 시작되었다고 보겠습니다. convenience 이니셜라이저는 현재 어떤 프로퍼티도 변경할 수 없어요. 같은 클래스의 지정 이니셜라이저로 갑니다.
지정 이니셜라이저는 현재 서브클래스의 모든 프로퍼티 값이 초기화 되었는지 확인합니다. (safety check1) 그 후 슈퍼클래스의 지정 이니셜라이저로 체인을 타고 올라갑니다.
슈퍼클래스의 지정 이니셜라이저도 모든 슈퍼클래스의 프로퍼티 값이 초기화되었는지 확인하고 더이상 올라갈 체인이 없으면 (슈퍼클래스의 슈퍼클래스가 없으면) 모든 프로퍼티 값이 초기화되고, 메모리도 완전히 초기화 된 것 입니다. - Phase1 끝
이제 슈퍼클래스의 지정 이니셜라이저는 인스턴스를 추가로 커스터마이징 할 수 있어요. 슈퍼 클래스의 커스터마이징이 끝나면, 서브 클래스의 지정 이니셜라이저가 커스터마이징 할 수 있습니다. (안해도 됨) 마지막으로 서브클래스의 지정 이니셜라이저의 커스터마이징이 끝나면 원래 호출되었던 convenience 이니셜라이저가 추가적인 커스터마이징을 할 수 있게 됩니다. Phase1의 과정과는 반대로 내려오는 거죠 :) - Phase2 끝
Failable Initializers
참고 자료 : https://wlaxhrl.tistory.com/49
초기화를 하다가 실패할 경우가 있는 경우는
init?()
과 같은 형식으로 failable init을 할 수 있습니다.failable initializer는 초기화하는 타입의 옵셔널 값을 생성합니다.
init?(species: String) { if species.isEmpty { return nil } self.species = species }
다음과 같이 실패할 경우에는 nil을 반환하도록 해 줍니다.
enum TemperatureUnit { case kelvin, celsius, fahrenheit init?(symbol: Character) { switch symbol { case "K": self = .kelvin case "C": self = .celsius case "F": self = .fahrenheit default: return nil } } }
enum에서는 해당하는 경우가 없을 때, 초기화에 실패하고 싶을 때 사용할 수 있어요 :)
Required Initializers
클래스의 이니셜라이저에 required 수식어를 붙이면 해당 클래스를 상속받는 모든 서브클래스는 해당 이니셜라이저를 구현해야 합니다. override 키워드는 안 붙여도 OK.
사실 initialization은 아직 내용이 좀 더 많은데, 다음에 추가로 써보겠습니다 🙇🏻♀️
'swift' 카테고리의 다른 글
Collections Types - Set (0) 2021.04.08