ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Apple Dev Reference - Swift] Failable Initializer
    앱등이에게 살충제를 뿌린다./Apple Dev Reference 2016. 3. 30. 23:59

    Apple Developer 문서를 번역만 하였습니다.


    Failable Initializers

    가끔은 초기화가 실패할 가능성이 있는 클래스, 구조체, 열거형(enum)을 만드는 일이 유용할 때가 있습니다. 초기화 실패는 부적절한 초기화 매개변수 사용이나 필수적인 리소스가 없어서 발생할 수 있습니다. 초기화를 방해할만한 다른 요소들도 있을 수 있구요.


    이런 초기화 실패를 적절히 대처하기 위해서 1개 이상의 Failable Initializer를 만들어 주세요. init뒤에 물음표()를 붙임으로써 Failable initializer를 만들 수 있습니다. 

     Note : 같은 파라미터의 타입과 이름을 갖는 failable 과 nonfailable은 동시에 만들 수 없습니다. 


    Failable Initializer는 옵셔널타입을 생성합니다. Failable Initializer에서 초기화가 실패할 수 있는 위치에 return nil을  적어주면 됩니다.

     Note : 좀 자세히 말하자면, Initializer는 값을 리턴하지 않습니다. Initializer의 역할은 이 초기화 메소드가 끝나면서 self가 완전히, 정확히 초기화 되는 것을 보장하는 것입니다. 여러분이 비록 Failable Initializer에서 return nil을 적어주긴 했지만 보통의 초기화에서 초기화가 잘되었다는 의미로 return이라는 키워드를 사용하지는 않습니다.


    아래의 예제는 String상수 프로퍼티 species를 갖는 Animal 구조체를 정의하는 예제입니다. Animal구조체에서도 species:String을 파라미터로 갖는 Failable Initializer를 사용하고 있습니다. 이 Initializer는 species가 Empty한지 확인을 하고 있습니다. 그리고 만약 Empty하다면 초기화 실패가 발생하죠. Empty하지 않다면 초기화는 성공을 하게됩니다.


    이 Failable Initializer를 새로운 Animal 인스턴스를 생성하면서 사용해보도록 합시다.


    Failable Initializer의 파라미터인 species에 빈 문자열을 넣으면 초기화 실패가 일어나게 됩니다. 아래처럼요



    Failable Initializers for Enumerations

    열거형 타입에서 적절한 값을 얻기 위해 Failable Initializer를 사용할 수 있습니다. 입력된 매개변수가 존재하는 열거형 값에 매치되지 않는다면 초기화는 실패하게 됩니다. 


    아래의 예제는 3개의 상태를 갖는 TemperatureUnit이라는 열거형을 정의하고 있습니다. Character 하나와 이 열거형에서 적절한 값을 매치해주기 위해 Failable Initializer를 사용하고 있습니다. (이 Character를 온도를 표시하는 Symbol이라고 생각하시면 될 것 같네요.)


    Failable Initializer를 사용해 세 가지 값중 적절한 값을 선택할 수 있게 되었습니다. 동시에 파라미터가 열거형의 값에 매치되는 값이 아니라면 초기화 실패를 일으키게 되구요.



    Failable Initializers for Enumerations with Raw Values

    rawValue를 사용하는 열거형에는 자동으로 Failable Initializer(init?(rawValue:))가 생성됩니다. 그리고 이 rawValue가 자연스레 Failable Initializer의 파라미터가 됩니다. 입력된 파라미터 rawValue가 열거형에서 매칭되는 값을 찾지 못하면 초기화는 실패하게 됩니다.

    위의 TemperatureUnit예제를 init?(rawValue:)의 장점을 살려 아래와 같이 다시 쓸 수 있겠네요.



    Propagation of Initialization Failure

    클래스, 구조체, 열거형의 Failable Initializer는 같은 클래스, 구조체, 열거형 내에 있는 다른 Failable Initializer에 delegation할 수 있습니다. 유사하게 서브클래스의 Failable Initializer 또한 부모클래스의 Failable Initializer로 delegation할 수 있습니다.

    만약 delegation 도중에 초기화 실패가 발생한다면 이 초기화 과정은 그 즉시 실패가 되어 더 이상의 초기화 코드는 수행되지 않습니다.

    **delegation : 아래의 예제에 나오듯 init안에서 다른 init을 불러서 수행한다고 이해하시면 될듯합니다~

    Note : Failable Initializer는 nonfailable initializer의 delegation이 가능합니다. 따라서 이미 존재하는 초기화 구문에서 Failable한 상황을 추가하고자 하신다면 이 방법을 사용하시면 됩니다.


    아래의 예제는 Product라는 클래스의 서브클래스인 CartItem클래스를 정의하고 있습니다.


    CartItem클래스의 Failable Initializer는 초기화 파라미터인 quantity의 값이 1 이상인지를 확인하고 있습니다. quantity값이 이 조건을 만족하지 못하면 이 초기화 과정은 그 즉시 실패로 처리되고 더 이상의 코드는 실행되지 않습니다. Product클래스의 Failable Initializer 또한 name의 값을 확인하고 있습니다. 유사하게 name이 빈 문자열이라면 초기화과정은 즉시 실패로 처리됩니다.


    CartItem클래스를 nonempty 문자열과 1 이상의 숫자로 name과 quantity를 초기화하면 초기화는 성공하게 됩니다.


    만약 CartItem클래스를 초기화하면서 quantity에 0을 전달하게 되면 초기화실패가 일어나게 됩니다.


    유사하게 빈 문자열을 name에 전달하게 되면 초기화실패가 일어나게 됩니다.



    Overriding a Failable Initializer

    서브 클래스에서 상위 클래스의 Failable Initializer도 다른 Initializer처럼 오버라이드 할 수 있습니다. 또한 상위 클래스의 Failable Initializer를 오버라이드하여 서브 클래스에서는 Nonfailable Initializer로 사용할 수 있습니다. 이를 통해 상위 클래스에서는 초기화 실패가 일어나지만 서브 클래스에서는 초기화 실패가 일어나지 않게 하는 것이 가능합니다. 


    상위 클래스의 Failable Initializer를 Nonfailable Initializer로 오버라이드 하게되면 상위 클래스의 Initializer로 delegation하는 방법은 상위 클래스의 Failable Initializer 결과를 강제 해제(forced unwrapping) 하는 방법밖에 없다는걸 알아두세요.

     Note : Failable Initializer를 Nonfailable Initializer로 오버라이드할 수는 있지만 반대의 경우는 안됩니다.


    아래의 예제는 Document클래스를 정의하고 있습니다. 이 클래스를 초기화하는데에 필요한 name이라는 프로퍼티에는 문자열 또는 nil이될 수 있습니다. 단 빈 문자열("")은 될 수 없습니다.


    다음 예제는 Document 클래스를 상속받는 AutomaticallyNamedDocument클래스를 정의하고 있습니다. AutomaticallyNamedDocument클래스는 Document클래스의 두 개의 Designated Initializer를 오버라이딩하고 있습니다. 이 오버라이드를 통해 name에 빈 문자열이나 nil로 초기화되는 객체의 name은 "[Untitled]"가 됩니다. 


    AutomaticallyNamedDocument클래스는 상위 클래스의 Failable Initializer인 init?(name:)을 Nonfailable Initializer로 오버라이딩하고 있습니다. AutomaticallyNamedDocument클래스가 상위 클래스와 달리 빈 문자열에 대한 대처를 해놓았기 때문에 더이상의 초기화 실패는 일어나지 않게 되었고 Nonfailable한 버전의 초기화가 그 자리를 대신하게 되었습니다.


    상위 클래스의 Failable Initializer를 오버라이딩하여 Nonfailable Initializer를 구현할 때, 강제 해제(forced unwrapping)를 사용할 수도 있습니다. 예를 들어, Document의 하위 클래스 UntitledDocument에서는 항상 "[Untitled]"라는 name을 갖고 Failable Initializer인 상위 클래스의 init(name:)를 호출하고 있습니다.


    이런 경우에, 상위 클래스의 init(name:)에서 빈 문자열로 초기화를 시도하면 강제 해제(forced unwrapping)이 런타임 에러를 일으키게 됩니다. 하지만 위의 경우는 항상 상수 문자열로 초기화를 시도하기 때문에 초기화 실패는 일어나지 않고, 런타임 에러 또한 일어날 가능성이 없습니다.



    The Init! Failable Initializer

    보통의 경우에는 옵셔널 객체를 생성하는 Failable Initializer를 정의하면서 init 뒤에 물음표를 사용합니다(init?). 하지만 명시적으로 Unwrapped 옵셔널 객체를 리턴하는 Failable Initializer를 선언할 수도 있습니다. 이 경우에는 init 뒤에 느낌표를 사용해줍니다(init!). 


    init?에서 init!으로 delegation도 가능하고 반대로도 가능합니다. init?에서 init!으로 오버라이딩도 가능하고 반대로도 가능합니다. init에서 init!으로도 delegation이 가능합니다. 물론 이 단계에서 init!이 초기화 실패를 발생시켜서 assertion이 발생할 수는 있겠지만 말입니다.




    <Fail하면 nil을 리턴한다는 건.. 우리 삶에도 적용이 되는듯 합니다.>



    출처 : https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html


Designed by Tistory.