swift-11 초기화

영점·2022년 10월 6일
0

Swift_Study

목록 보기
11/12

스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 초기화 입니다.

초기화는 내용도 많고 난이도도 높은 것 같습니다..
일단 기초적인 내용은 다 추가하였습니다.
심화적인 부분? org 기준 후반부분은 추가가 필요합니다.

정의

초기화 (Initialization)는 인스턴스의 클래스, 구조체, 또는 열거형을 사용하기 위해 준비하는 단계이다.

이 단계에는 인스턴스에 각 저장된 프로퍼티에 초기값을 설정하고, 새로운 인스턴스가 사용할 준비가 되기 전에 다른 설정이나 초기화를 수행하는 것을 포함한다.

초기화 선언

init() {
    // perform some initialization here
}

구조체 & 클래스의 경우

  1. 선언과 동시에 값을 주어 초기화를 하거나
  2. ?(옵셔널)를 주어 초기화를 하거나
  3. init(생성자)를 주어 초기화를 하는 방법이있다.

해당 예시에서는 3번 방법을 사용하였다. ( 구조체 )

struct Fahrenheit {
    var temperature: Double
    init() { 
        temperature = 32.0
    }
}

var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// Prints "The default temperature is 32.0° Fahrenheit"

프로퍼티가 항상 같은 초기값을 갖는다면 초기화 구문 내에서 값을 설정하기보다 기본값을 제공하라
이것은 초기화 구문을 더 짧고, 명확하게 하고 기본값으로 부터 프로퍼티의 타입을 유추할 수 있다.

초기화는 모든 프로퍼티가 값을 가져야만 초기화에 성공해서 인스턴스를 생성한다.

그런데 구조체는 프로퍼티의 초기화를 따로 지정하지 않을 경우, 초기화되지 않은 프로퍼티를 초기화할 수 있는 init 함수를 자동으로 제공하기 때문에 이렇게 할 수 있다. ( memberwise initializers )

struct Fahrenheit {
    var temperature: Double
}

var f = Fahrenheit(temperature: 32.0)
print("The default temperature is \(f.temperature)° Fahrenheit")
// Prints "The default temperature is 32.0° Fahrenheit"

하지만 클래스의 경우 memberwise initializers를 제공하지 않아서.. 초기값 지정을 구조체와는 좀 다르게 해줘야 한다.

크게 두가지로 나뉘는데,

Designated Initializers (지정생성자) : 클래스의 모든 프로퍼티를 초기화 하는 생성자

지정 생성자의 경우 옵셔널 변수로 선언 되지 않았거나, 기본 값을 갖지 않는 프로퍼티가 있을 경우엔 생성자를 추가하여 초기값을 지정한다. 또한 상속받은 서브 클래스에서 지정 생성자를 작성할 경우, 반드시 슈퍼 클래스의 Initializers를 호출해주어야 한다.

class Fahrenheit {
    let temperature : String
    
    init(temperature : String) {
        self.temperature = temperature
    }
}
 
class Temp : Fahrenheit {
    var feel : String
    
    init(feel : String) {
        self.feel = feel
        super.init(temperature : feel)
    }
}

Convenience Initializers (편의 생성자) : Designated Initializers를  도와주는 역할

Convenience Initializers는 보조 생성자라, 굳이 모든 프로퍼티를 초기화 할 필요가 없다.
허나, 만약 초기화가 안 된 프로퍼티가 있는 경우 지정 생성자가 모든 프로퍼티를 초기화 시켜서
최종적으로 같은 클래스 내에 있는 지정 생성자를 호출 시키기 때문에,
아무리 보조 생정자를 호출해도 어짜피 마지막에 지정 생성자를 호출하기에 신경쓰지 않아도된다.

class Fahrenheit {
    let temperature : String
    let feels : String
    
    init(temperature : String, feels : String) {
        self.temperature = temperature
        self.feels = feels
    }
    
    convenience init(temperature : String) {
        self.init(temperature: temperature, feels: "cold")
    }
}

예시로 설명하자면, Convenience 생성자가 먼저 실행되고 그 다음 최종적으로 Designated 생성자가 실행되는 순서인 것이다.
그렇기 때문에 꼭 Designated 생성자가 생성되어 있어야 에러가 나지 않으니 주의!!

( 내용이 좀 많이 어렵다.. Convenience랑 Designated 이해하는데 시간 오래걸림.. )

클래스의 2단계 초기화 ( 클래스의 초기화 프로세스 )

2단계 초기화는 프로퍼티 값이 초기화 되기전에 접근하는 것을 막고 다른 초기화 구문이 예기치 않게 다른 값을 설정하는 것을 막는다. 때문에 초기화를 보다 안전하게 진행하기 위해 2단계 초기화를 사용하는 것이다.

예시로 1단계와 2단계를 해설해보겠다.

class Human {
    var name: String
    
    init(name: String) {
        self.name = name
        
        print(name)
    }
}
 
class Developer: Human {
    var language: String
    
    init(language: String) {
        self.language = language
        super.init(name: "nothing")
        
        print(language)
    }
}
 
class Dtzero : Developer {
    var nickName: String
    
    init(nickName: String) {
        self.nickName = nickName
        super.init(language: "swift")
        
        print(nickName)
    }
}

1단계 초기화

위의 예시에서 Dtzero란 클래스의 인스턴스를 생성하기 위해 생성자를 부르게 되면

Dtzero(nickName: "well")

Dtzero → Developer → Human 순서로 자신의 프로퍼티를 초기화하고
super.init을 통해 부모 클래스까지 초기화 시키면 1단계 초기화는 끝이난다.

2단계 초기화

모든 프로퍼티가 기본 값을 가지게 되어 초기화가 완료 되었다면,
가장 마지막으로 호출됐던 제일 상위 클래스의 남은 작업부터 차례로 아래로 실행된다. ( 스택 )
해서 위의 Dtzero란 클래스의 인스턴스를 생성하기 위해 생성자를 부르면 이렇게 출력 된다.

/*
nothing
swift
well
*/

nothing이 Human 클래스의 name이고, swift가 Developer 클래스의 language, well이 Dtzero 클래스의 nickName이다. ( 2차 초기화 과정에서 값들이 덮어 씌워진 것 )

상속의 재정의

Convenience는 상속은 되지만 재정의가 불가능
자식 클래스에서 재정의 초기화 할 때 반드시 super.init 필수!

부모 클래스의 모든 지정 초기화를 상속받거나,
부모 클래스의 모든 지정 초기화를 오버라이딩 해서 구현 했다면
이땐 부모의 모든 편의 초기화 메서드가 상속된다.

필수 초기화 구문

클래스 초기화 구문 앞에 required를 작성하여 클래스의 모든 하위 클래스가 해당 초기화 구문을 구현해야 함을 나타낸다. 추가로 지정된 필수 초기화 구문을 재정의 할 때 override를 작성하지 않는다.

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

class SomeSubclass: SomeClass {
    required init() {
        // subclass implementation of the required initializer goes here
    }
}
profile
일단 배운내용은 적어두기

0개의 댓글