Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[swift] 18. 초기화(initialize) 심화 개념 본문

swift 5 문법

[swift] 18. 초기화(initialize) 심화 개념

jake-kim 2020. 10. 1. 00:19

Default Initialize (기본 이니셜라이저)

클래스에서 이니셜라이저를 정의하지 않은 경우

Designated Initializers and Convenience Initializers

Designated Init(지정초기화): 클래스의 모든 프로퍼티를 초기화

Convenience Init: 미리 지정된 값을 사용해서 최소한의 입력으로 초기화 할 수 이도록 해주는 초기자

 

  • Designated는 반드시 위임을 superclass로 해야하고, convenience는 같은 레벨에서 해야함
  • 클래스 타입은 반드시 한 개 이상의 Designated Initialize가 있어야 함
  • Designated Initilize는 반드시 직계 superclass의 지정 init을 호출해야함
  • Convenience Initilize에서는 반드시 Designated init이 호출되어야 함

2단계 초기화 (Two-Phase Initialization)

 - 원래 스위프트에서 클래스는 2단계를 통해서 초기화 됨

첫 번째 단계: 각 저장된 프로퍼티를 정해진 초기값으로 초기화

두 번째 단계: 새로운 인스턴스의 사용이 준비됐다고 아렬주기 전에 저장된 프로퍼티에 대해 커스터마이징

2단계 초기화를 위한 safety-check

1단계) Designated Init은 superclass의 초기자에게 위임하기 전에 모든 프로퍼티를 초기화 해야함

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

* 핵심: super.init호출 할 경우, 현재 클래스의 초기화를 중단한 후 메모리에 올려짐 (메모리에 올려질 때 초기화가 되어있어야 오류x)

// Designated Init을, 프로퍼티 초기화 한 후 부른 적합한 경우

class A {
    init () {}
}

class B: A {
    let a: Int

    init(i: Int) {
        a = i
        super.init() // success
    }
}

2단계) 서브클래스에서 상속된 값을 사용하기 전에 반드시 superclass의 초기자로 위임을 넘겨야 함

(super.init을 호출하지 않으면 아직 super클래스가 초기화 되지 않은 상태이므로, 상속된 값을 사용할 수 없음)

// superclass를 먼저 초기화 하지 않고 상속된 값을 사용하려고 한 경우
class A {
    let b = 2

    init () {}
}

class B: A {
    let a: Int

    init(i: Int) {
	a = 1
	print(b) // error: b의 값이 아직 초기화 되지 않음
	super.init()
    }
}
// superclass의 초기자에게 위임하기 전에 모든 프로퍼티를 초기화 하지 않은 경우
class A {
    let b = 2

    init () {}
}

class B: A {
    let a: Int

    init(i: Int) {
        super.init() // error
        a = 1
	print(b)
    }
}

 

// 적절하게 초기화한 경우: super.init하기 전에 본 클래스의 프토퍼티 모두 초기화, 상속된 값을 사용하기 전에 superclass 초기화
class A {
    let b = 2

    init () {}
}

class B: A {
    let a: Int

    init(i: Int) {
        a = 1
        super.init()
        print(b)
    }
}

3단계) convenience Init은 반드시 어떤 프로퍼티를 할당하기 전에 다른 초기자로 위임을 넘겨야 함

(convenience init 후 다른 초기자로 위임을 넘기면, convenience init한 값이 묻힘)

class A {
    let b = 2

    convenience init(abc: Int) {
        b = abc // error: 여기서 초기화 해도, self.init()에서 다시 2로 덮어씌워짐
        self.init()
    }
}
class A {
    var b = 2

    convenience init(abc: Int) {
        self.init()
        b = abc // success
    }
}

4단계) 1단계를 하지 않으면 self로 property나 메서드를 참조할 수 없음

class A {
    var b = 2

    convenience init(abc: Int) {
        self.b = 1 // error
        self.init()
    }
}

상속과 오버라이딩

- subclassd에서 superclass의 이니셜라이저를 상속받지 않음

(무분별하게 상속되어, subclass에서 잘못 초기화 되는 것을 막기 위함)

직접 override키워드를 붙여야 함

class A {
    var a: Int

    init() {
        a = 1
    }

    init(a: Int) {
        self.a = a
    }

    convenience init(abc: Int) {
        self.init()
        a = abc
    }
}

class B: A {
    let b: Int

    init(a: Int, b: Int) {
        self.b = b
        super.init(a: a)
    }

    override convenience init(abc: Int) {
        self.init(a: a, b: 123)
    }
}

Failable Initializers

- 초기화시, 실패할 수 있는 초기화 (성공시 Optional값 반환, 실패시 nil반환)

- 이 초기화 역시 override이 가능

class A {
    var a: Int
    init?(b: Int) {
        if b == 0 { return nil }
        self.a = b
    }
}

print(A(b: 0)) // nil

클로저를 이용하여 메서드 or 프로퍼티 값 초기화

- 클로저 끝에 괄호"()" 사용

- 클로저를 초기자에서 사용하면 self를 통해 달느 프로퍼티를 사용 불가

(클로저 실행 시점에서의 다른 프로퍼티들은 초기화가 끝난 시점이 아니기 때문)

class A {
    var a: Int = {
        return 123
    }()
}

Required init

- 서브클래스에서 꼭 선언해야 하며, 구현은 하지 않아도 됨

 

Comments