ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • swift) Initialization
    swift 2021. 6. 19. 16:29

     

    역시 기초가 제일 중요하고, 어려운 것 같아요 😂오늘은 swift의 initialization에 대해 알아봅시다. 

    Two-Phase Initialization

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

     

    Initialization — The Swift Programming Language (Swift 5.5)

    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

    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를 호출 가능

    스크린샷 2021-01-29 오후 10 05 44

     

    예를 들어, 위의 서브클래스의 convenience 이니셜라이저 호출로 시작되었다고 보겠습니다. convenience 이니셜라이저는 현재 어떤 프로퍼티도 변경할 수 없어요. 같은 클래스의 지정 이니셜라이저로 갑니다.

    지정 이니셜라이저는 현재 서브클래스의 모든 프로퍼티 값이 초기화 되었는지 확인합니다. (safety check1) 그 후 슈퍼클래스의 지정 이니셜라이저로 체인을 타고 올라갑니다.

     

    슈퍼클래스의 지정 이니셜라이저도 모든 슈퍼클래스의 프로퍼티 값이 초기화되었는지 확인하고 더이상 올라갈 체인이 없으면 (슈퍼클래스의 슈퍼클래스가 없으면) 모든 프로퍼티 값이 초기화되고, 메모리도 완전히 초기화 된 것 입니다. - Phase1 끝

     

     

    스크린샷 2021-01-29 오후 10 05 58

     

    이제 슈퍼클래스의 지정 이니셜라이저는 인스턴스를 추가로 커스터마이징 할 수 있어요. 슈퍼 클래스의 커스터마이징이 끝나면, 서브 클래스의 지정 이니셜라이저가 커스터마이징 할 수 있습니다. (안해도 됨) 마지막으로 서브클래스의 지정 이니셜라이저의 커스터마이징이 끝나면 원래 호출되었던 convenience 이니셜라이저가 추가적인 커스터마이징을 할 수 있게 됩니다. Phase1의 과정과는 반대로 내려오는 거죠 :) - Phase2 끝 

     

    Failable Initializers

    참고 자료 : https://wlaxhrl.tistory.com/49

     

    Initialization (3/3)

    Apple 제공 Swift 프로그래밍 가이드(3.0.1)의 Initialization 부분을 공부하며 정리한 글입니다. 개인적인 생각도 조금 들어가있습니다. 이니셜라이저 마지막 편입니다. Failable Initializers, Required In..

    wlaxhrl.tistory.com

     

    초기화를 하다가 실패할 경우가 있는 경우는 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

    댓글

Designed by Tistory.