Swift에서 Struct, Class, Enum을 초기화할 수 있는 Initializer에 대해서 알아보자. Deinitializer는 class에서만 사용이 가능하다. 이 부분에서 class 초기화는 다른 언어의 초기화 과정과 약간 다른 부분이 있다. 해당 내용은 좀 길어질 수 있어 다음 포스팅으로 넘겼다. 이번에는 Value type, Reference type의 초기화 과정에 대해 개괄적이 내용을 알아보는 것을 목적으로 한다. 그럼 시작하자.

Initializer

  • class, struct, enum 사용 가능

  • stored property 초기화 및 instance 사용 위한 초기화

  • initailier라는 method 사용

  • return value 없음

    • Objective-C의 경우 있음
  • class의 경우 init, deinit을 쌍으로 가짐

  • Optional Property의 경우 반드시 초기화할 필요 없음

    • nil로 초기화
  • Constant(let) Property는 initialization 과정에서 반드시 초기화 되어야 함

    • 이후 변경 불가
  • struct의 경우, init을 따로 정의하지 않으면 자동으로 memberwise initializer가 만들어짐

  • failable initializer

    • init?으로 사용가능함

    • 잘못된 parameter나 외부 resource가 존재하지 않는 경우

    • override 해서 non-failable initializer로 만들 수 있음

      class Document {
          var name: String?
          init() {}
          init?(name: String) {
              if name.isEmpty { return nil }
              self.name = name
          }
      }
      
      class AutomaticallyNamedDocument: Document {
          override init(name: String) {
              super.init()
              if name.isEmpty {
                  self.name = "[untitled]"
              } else {
                  self.name = name
              }
          }
      }
  • Closure를 통한 default property value setting이 가능함

    • class, struct 가능
    class SomeClass {
        let someProperty: SomeType = {
            // setting
            return someValue
        }
    }

Value Type Initialization

struct Size {
    var width = 0.0, height = 0.0
}

let a = Size(width: 3, height: 4)
let b = Size(width: 10)

print(a.width, a.height)
print(b.width, b.height)
  • Struct의 경우 자동으로 memberwise initailizer를 갖는다.
  • 이게 뭐냐
    • 커스텀 생성자를 지정하지 않았을 경우에 지가 알아서 만들어주는 것
    • 처음에 값을 지정해줬을 때에도 동작,
    • 이미 값을 지정해줬다면 특정 프로퍼티만 초기화하는 것도 가능
struct Point {
    var x = 0.0, y = 0.0
}

struct Rect {
    var origin = Point() // 0.0, 0.0
    var size = Size()
    
    // init() {}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }

    init(center: Point, size: Size) {
        let originX = center.x - size.width/2
        let originY = center.y - size.height/2
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}
  • 위에서는 명시된 생성자가 없을 떄, 프로퍼티가 선언되어 있어 Size()도 생성이 되었지만
  • 지금과 같은 경우 init을 명시적으로 작성했다면, 추가적으로 선언되지 않는다.
    • Rect()는 생성되지 않는다는 것
    • 만약 사용하고 싶다면 추가적으로 선언해주어야 한다.

Class Initialization

  • 모든 stored property는 initilization과정에서 초기화되어야 함
  • 상속 관계의 모든 superclass도 마찬가지
  • 이러한 상속 관계에서 역시 모든 property를 초기화하는 것을 보장하기 위해 두가지 형태의 initializer를 가짐
    • Designated initializer
      • Primary initializer
      • 모든 property를 초기화 시키는 역할을 함
      • Superclass의 property를 초기화하기 위해 super의 init을 사용
      • 모든 class는 하나 이상의 지명 생성자를 가짐
      • 보통 1개임
    • Convenience initializer
      • Secondary initializer
      • 일부 property에서 default값을 사용할 때 사용
      • 내부에서 designated intializer를 호출함
      • 특정 usecase에서 사용될 객체를 생성할 때 사용
        convenience init(parameters) {
            //statements
        }
  • Delegation Rule
    • Designated initializer는 반드시 위 super의 designated initializer를 호출해야함
    • convinience initializer는 반드시 그 class의 다른 initializier를 호출해야 함
    • convenience intializer는 최종적으로 designatied initializer를 호출하는 형태어야 함
  • Two-Phase Initialization
    • Phase 1
      • initializer 호출됨
      • 새로운 instance를 위한 memory가 alloc됨
        • memory 초기화는 아직임
      • designated intiailizer가 모든 stored property에 대한 초기화 진행
      • designated initializer가 super class의 initializer를 호출
      • root class까지 반복 호출
      • 모든 stored property가 초기화되고 phase 1 종료
    • Phase 2
      • root class로부터 역으로 수행
      • 모든 class는 instance를 customizer할 수 있음
      • self에 access하고, property 수정하고, instance method 호출 가능
    • 보다 자세한 설명은 다음 글에 작성하도록 하겠음
    • 이렇게 하는 이유는, 모든 stored property를 초기화하고, 그 다음에 값을 변경하도록 하기 위함임
  • require initializer
    • 모든 subclass들이 구현해야 하는 initializer
    • required 사용
    • overriding시에도 required 사용해야 함

Deinitialization

  • intance dealloc 되기 직전에 호출됨
  • deinit 키워드 사용
  • class에서만 사용 가능
  • 통상적으로는 구현할 필요 없음
profile
Goal, Plan, Execute.

0개의 댓글