Swift 정리 - Initialization

김세영·2022년 3월 14일
0

Swift Doc 정리

목록 보기
13/17
post-thumbnail

초기화 (Initialization)
클래스, 구조체, 열거형 인스턴스를 사용하기 위해 준비 작업을 하는 단계

  • init 키워드로 초기화 구문 생성
  • 저장 프로퍼티의 초기값을 설정
  • 값을 반환하지 않음
  • 초기화와 반대로 사용이 끝난 인스턴스를 해제할 때 마무리 작업을 수행 가능 (deinit 키워드 사용)

Setting Initial Values for Stored Properties

인스턴스의 저장 프로퍼티는 사용 전 반드시 초기화되어야 함

  • init 내부에서 저장 프로퍼티의 값 설정 시
    프로퍼티 옵저버가 호출되지 않고 할당이 수행됨
struct Farenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
var f = Farenheit() // init() 실행
// var f = Farenheit.init()
print("Default temperature is \(f.temperature)"
// Default temperature is 32.0

Default Property Values

프로퍼티의 선언과 동시에 값을 할당하여 초기화할 수 있다.

  • 항상 같은 초기값을 갖는다면 기본 프로퍼티를 사용하는 것이 좋다.
  • 타입을 명시하지 않아도 타입 추론을 통해 결정된다.
  • 기본값은 상속 시 함께 상속된다.

Customizing Initialization

초기화 구문에 파라미터를 정의해 사용할 수 있다.

  • init(parameterA:parameterB:) or init(_:), ...
  • 프로퍼티의 최초 값이 없고 나중에 추가될 수 있는 값은 옵셔널로 선언할 수 있다.
    • 자동으로 nil로 초기화되어, 해당 프로퍼티는 초기화 구문 내에서 초기값을 넣어주지 않아도 된다.
  • 초기화 구문에는 상수 프로퍼티(let)에 값을 할당하는 것을 허용한다.
class SomeClass {
    let someIntConstant: Int
    var someIntVariable: Int
    var someString: String? // 자동으로 초기값 nil 대입
    
    init(constant someIntConstant: Int, _ someIntVariable: Int) {
        self.someIntConstant = someIntConstant // let 할당 허용
        self.someIntVariable = someIntVariable
    }
}

let someClass = SomeClass(constant: 10, 30)
someClass.someIntConstant // 10
someClass.someIntVariable // 30
someClass.someString // nil

Default Initializers

모든 프로퍼티의 초기값이 설정되어 있고, init 구문이 하나도 없다면
모든 프로퍼티의 초기값을 기준으로 인스턴스를 생성하는 기본 초기화 구문을 제공

class SomeClass {
    var stringA: String?
    var stringB = "string B"
    var intC = 1
}
let someClass = SomeClass()
someClass.stringA // nil
someClass.stringB // string B
someClass.intC // 1

Memberwise Initializers for Struct Types

구조체는 init 구문을 하나도 사용하지 않았다면, 모든 프로퍼티를 인자로 하는 생성자를 자동으로 생성

struct SomeStruct {
    var someBool = false
    var someInt: Int
}
let someStruct = SomeStruct(someBool: true, someInt: 1)
// or
let someStruct = SomeStruct(someInt: 2) // someBool은 초기값이 존재하므로 따로 초기화하지 않아도 된다.

Initializer Delegation for Value Types

초기화 구문에서 다른 초기화 구문을 호출 가능, 이를 이니셜라이저 위임 (Initializer Delegation)이라 한다.

  • 값 타입 (struct, enum)

    • 상속을 지원하지 않기 때문에 자기 자신의 다른 이니셜라이저만 사용 가능
    • self.init을 사용하여 다른 이니셜라이저를 호출할 수 있다.
    • self.init은 이니셜라이저 내부에서만 사용할 수 있다.
    • 커스텀 초기화 구문을 정의했다면, 기본 이니셜라이저(구조체의 경우 Memberwise Initializer)을 사용할 수 없다.
      • 이는 인스턴스 생성 시 사용자가 기술한 반드시 필요한 작업을, 기본 이니셜라이저를 사용함으로써 우회하는 것을 방지한다.
      • 이를 허용하려면 커스텀 이니셜라이저를 extension에서 구현해야 한다.
  • 참조 타입 (class)

    • 상속이 있으므로 Superclass의 이니셜라이저를 Subclass의 이니셜라이저에서 호출 가능 (super.init(...))
    • 상속되는 모든 저장 프로퍼티에 적절한 값이 들어가도록 초기화해야 할 책임이 있다. (아래 Class Inheritance and Initialization 참조)

Class Inheritance and Initialization

클래스의 모든 저장 프로퍼티와 상속받은 모든 프로퍼티는 초기화 단계에서 반드시 초기값이 할당되어야 한다.

Convenience Initializers

편의 이니셜라이저(Convenience Initializer)는 초기화 단계에서 미리 지정된 값을 사용해서 최소한의 입력으로 초기화를 할 수 있도록 해주는 이니셜라이저

  • init 앞에 convenience 키워드를 붙여 선언할 수 있다.

Initializer Delegation for Class Types

지정 이니셜라이저(Designated Initializer)와 편의 이니셜라이저 사이의 관계를 단순하기 위해 이니셜라이저 위임은 다음 세 가지 규칙을 따름

  1. 지정 이니셜라이저는 반드시 superclass의 지정 이니셜라이저를 호출
  2. 편의 이니셜라이저는 반드시 같은 클래스의 다른 이니셜라이저를 호출
  3. 편의 이니셜라이저는 반드시 궁극적으로 지정 이니셜라이저를 호출

다음 그림으로 이해할 수 있다.

Two-Phase Initialization

Swift에서 클래스 초기화는 2단계로 진행된다.

  1. 저장 프로퍼티는 초기값으로 설정
  2. 1이 완료된 후 저장된 프로퍼티를 커스터마이징

Objective-C의 Two-Phase Initialization

Swift의 2단계 초기화는 Objective-C와 유사하다.
차이점은 1단계에서인데, Objective-C는 모든 프로퍼티에 zero 혹은 null을 할당한다.
Swift에서는 커스텀 초기값을 할당할 수 있고, 0과 nil이 잘못된 초기값으로 지정된 경우 대처할 수 있다.

컴파일러는 Two-Phase Initialization이 에러 없이 끝나는 것을 보장하기 위해 4단계의 안전 확인(safety-check)을 한다.

Safety-Check 1

지정 이니셜라이저는 클래스 안에서 초기화를 Superclass의 이니셜라이저에게 위임하기 전에 모든 프로퍼티를 초기화해야 한다.

  • 메모리에서 객체는 모든 저장 프로퍼티가 초기 상태를 가져야 완전히 초기화된 것으로 간주하기 때문

Safety-Check 2

지정 이니셜라이저는 상속된 값을 할당하기 전에 Superclass의 이니셜라이저로 위임을 넘겨야 한다.

  • 그렇지 않으면 상속된 값이 Superclass의 이니셜라이저에 의해 덮어 쓰여지기 때문

Safety-Check 3

편의 이니셜라이저는 반드시 프로퍼티를 할당하기 전에 다른 이니셜라이저로 위임을 넘겨야 한다.

  • 그렇지 않으면 할당된 값이 다른 지정 이니셜라이저에 의해 덮어 쓰여지기 때문

Safety-Check 4

이니셜라이저는 초기화의 1단계가 끝나기 전에는 self의 값을 참조하거나 인스턴스 프로퍼티, 메서드 등을 호출하거나 읽을 수 없다.

Initializer Inheritance and Overriding

Subclass에서 Superclass의 이니셜라이저를 상속하지 않는다.

  • Superclass의 이니셜라이저가 무분별하게 상속되어 복잡해지는 것을 방지
  • 이니셜라이저를 오버라이드하려면 init 키워드 앞에 override 키워드를 붙인다.
  • Subclass의 이니셜라이저에서 Superclass의 상수 프로퍼티(let)는 변경할 수 없다.

Automatic Initializer Inheritance

특정 상황에서 Subclass는 Superclass의 이니셜라이저를 자동으로 상속받는다.

  • Subclass에서 지정 이니셜라이저를 정의하지 않으면 자동으로 Superclass의 모든 지정 이니셜라이저를 상속받는다.
  • Subclass가 Superclass의 지정 이니셜라이저를 모두 구현한 경우 (위의 규칙 포함) 자동으로 Superclass의 편의 이니셜라이저를 상속받는다.
    • 해당 규칙에 따라 Superclass의 지정 이니셜라이저를 Subclass의 편의 이니셜라이저로 구현 가능

Failable Initializers

초기화 과정 중 초기화에 실패할 가능성이 있는 이니셜라이저를 선언할 수 있다.

  • init?(...)의 형태로 사용
  • 이니셜라이저는 파라미터로 구분되기 때문에 실패 가능한 이니셜라이저와 그렇지 않은 이니셜라이저의 파라미터는 같지 않아야 한다.
  • 초기화 과정 중 실패한 부분에서 nil을 반환한다.
  • 이니셜라이저는 값을 반환하지 않기 때문에 실패를 나타내는 return nil이 존재하더라도, 성공하는 경우 값을 반환하지 않는다.
  • 실패가 발생하면 즉시 관련된 이니셜라이저가 중단된다.
    • 실패 가능한 이니셜라이저를 그렇지 않은 이니셜라이저에 위임할 수 있다.
    • 이런 방식으로 특정 상황에만 실패하는 이니셜라이저를 구현할 수 있다.

대표적인 init? 의 사용


Int의 이니셜라이저 중 하나로, StringInt로 변환하는 이니셜라이저이다.

let intFromString = Int("1")
위 코드에서 intFromString의 타입은 Int?가 된다.

let intFromString = Int("1")!
위 코드에서 intFromString의 타입은 Int이다. (강제 언래핑)

let intFromString = Int("a")
위 코드에서 intFromStringnil이다.

Overriding a Failable Initializer

Superclass의 실패 가능한 이니셜라이저를 Subclass의 실패 불가능한 이니셜라이저로 오버라이딩 가능 (반대는 불가능)

  • init?init!으로 오버라이딩할 수 있다.

Required Initializers

모든 Subclass에서 반드시 구현해야 하는 이니셜라이저는 required 키워드를 붙여 선언할 수 있다.

class SomeClass {
    required init() { ... }
}

class SomeSubclass: SomeClass {
    required init() { ... } // 무조건 구현해야 함
}

Setting a Default Property Value with a Closure or Function

단순 값 할당이 아닌 복잡한 계산을 필요로 한다면, 클로저나 함수를 이용해 초기화 가능

  • 클로저 내부에서는 self나 다른 프로퍼티를 사용할 수 없다.
    • 클로저가 실행될 때 다른 프로퍼티의 초기화가 끝났다는 것을 보장할 수 없기 때문
class SomeClass {
    let someProperty: SomeType = {
        ...
        return someValue
    }()
}
profile
초보 iOS 개발자입니다ㅏ

0개의 댓글