초기화(Initialization)와 생성자(Initializer) 그리고 소멸자(Deinitialization)

썹스·2022년 10월 14일
0

Swift 문법

목록 보기
20/68

1. 초기화(Initialization)

초기화(Initialization)는 Custom Type(클래스, 구조체, 열거형)으로부터 인스턴스를 만드는 과정을 의미합니다.

클래스 또는 구조체로부터 인스턴스를 생성하려면 모든 프로퍼티에 기본값을 설정하거나, 옵셔널 타입으로 선언하여 nil 값으로 설정해야 합니다. 또는 init( )메서드를 사용해서 모든 프로퍼티의 저장 값을 설정해야 합니다. (프로퍼티에 저장값이 없으면 인스턴스를 생성할 수 없습니다.)

사실 프로퍼티에 기본값을 설정하여 사용하는 방법도 init( )메서드를 사용하게 되어있습니다. (개발자가 코드를 작성하지 않아도 자동으로 내부에 구현)

값을 직접 할당하여 초기화

class ABC{
    var a: Int = 0
    var b: Int = 0
    var c: Int = 0
  
//    init(){   // 프로퍼티에 초기값을 할당할 때 내부적으로 init( )메서드가 기본적으로 생성된다.
//        a = 0
//        b = 0
//        c = 0
//    }
}

var kim = ABC.init()    // ABC.init() == ABC()
kim.a  //0

내부적으로 init( ) 메서드를 사용하는 방법이기 때문에 이 방법 또한 지정 생성자의 한 종류이다.

이처럼 모든 프로퍼티에는 초기값이 할당되어야 하는데, 이러한 역할을 하는 것이 생성자(Initializer)입니다.



2. 생성자(Initializer)

모든 프로퍼티에 초기값을이 있어야 하며, 초기값(저장 속성)의 할당 역할을 하는 것이 생성자(Initializer)라 불립니다.

이러한 초기값(저장 속성)을 할당해주는 생성자(Initializer)는 상황에 따라 사용되는 종류가 다양합니다.

생성자의 종류클래스(Class)구조체(Struct)표현
지정 생성자(Designated Initializer)OOinit( )
편의 생성자(Convenience Initializer)O(상속 관련)X(상속 관련)convenience init( )
필수 생성자(Required Initializer)O(상속 관련)X(상속 관련)required init( )
실패가능 생성자(Failable Initializer)OOinit?( ) 또는 init( )!
멤버와이즈 생성자(Memberwise Initializer)XO-

📌 2-1. 지정 생성자(Designated Initializer)

  • 지정 생성자(Designated Initializer)는 가장 기본적이고 간단하게 사용할 수 있는 생성자입니다.

  • 클래스 또는 구조체에서 선언된 모든 프로퍼티에 기본값을 할당해야 하는데, 이러한 작업을 진행하는 생성자가 지정 생성자입니다.

상속없이 사용할 경우

class Color{
    var red: Int
    var blue: Int

    init(red: Int, blue: Int){   // 지정 생성자 사용
        self.red = red
        self.blue = blue
    }
}

클래스를 상속받아 사용할 경우

  • 지정 생성자는 상위 클래스의 지정 생성자를 호출해야 합니다. 이를 델리게이트 업(Delegate Up)이라 부릅니다.

  • 만약 하위 클래스에 프로퍼티가 새로 정의되면, 추가된 프로퍼티 또한 초기화해야 합니다.

  • Swift는 하위 클래스의 프로퍼티를 메모리 공간에 먼저 만들어야 하므로, 하위 클래스에 새로 정의한 프로퍼티를 먼저 작성해야 합니다. (self.yellow = yellow)

  • 하위 클래스의 프로퍼티를 메모리에 추가했다면, 상위클래스의 프로퍼티를 만들어야 합니다.

  • 하위 클래스에서는 상위 클래스의 프로퍼티를 만들 수 있는 권한이 없으므로, "super" 키워드를 사용하여 생성합니다. (super.init(red: red, blue: blue))
class Color1{
    var red: Int
    var blue: Int
   
    init(red: Int, blue: Int){
        self.red = red
        self.blue = blue
    }
}

class Color2: Color1{
    var yellow: Int
   
    init(red: Int, blue: Int, yellow: Int){
        self.yellow = yellow    //하위 클래스의 프로퍼티 먼저 생성!!
        super.init(red: red, blue: blue)
    }
}

📌 2-2. 편의 생성자(Convenience Initializer)

  • 편의 생성자(Convenience Initializer)자신이 속해있는 클래스의 지정 생성자를 호출해야 합니다. 이를 델리게이트 어크로스(Delegate Across)라고 부릅니다.

  • 초기화 과정을 간편하게 진행하기 위해 사용되며, 상속과 밀접한 관계를 가지고 있습니다.

  • 지정 생성자는 모든 프로퍼티를 초기화해야 하지만, 편의 생성자는 모든 프로퍼티를 초기화할필요가 없습니다.

  • 클래스에서 편의 생성자를 사용하려면 "convenience" 키워드를 사용해야 합니다.

  • 편의 생성자는 기본적으로 상속이 불가능 하지만, 일부 예외적인 상황에서는 상속이 가능합니다.

상속 없이 사용하는 경우

class Color{
    var red: Int
    var blue: Int
  
    init(red: Int, blue: Int){
        self.red = red
        self.blue = blue
    }
 
    convenience init(red: Int){
        self.init(red: red, blue: 0)
    }
}

클래스를 상속받아 사용할 경우

class Color1{
    var red: Int
    var blue: Int
  
    init(red: Int, blue: Int){
        self.red = red
        self.blue = blue
    }
}

class Color2: Color1{
    var yellow: Int
 
    init(red: Int, blue: Int, yellow: Int){
        self.yellow = yellow
        super.init(red: red, blue: blue)
    }

    convenience init(red: Int){
        self.init(red: red, blue: 0, yellow: 0)
    }

    override convenience init(red: Int, blue: Int){  // 상위 클래스의 지정 생성자를 재정의하여 사용
        self.init(red: red, blue: blue, yellow: 0)
    }
}

override convenience init(red: Int, blue: Int){ } --> 상위 클래스의 지정 생성자를 재정의하여 편의 생성자로 사용


📌 2-3. 필수 생성자(Required Initializer)

  • 필수 생성자(Required Initializer)는 생성자 앞에 "required" 키워드를 작성하여 사용하는 생성자입니다.

  • 상위 클래스에서 필수 생성자로 만들어진 생성자가 있으면, 하위 클래스에서 필수적으로 필수 생성자를 구현해야 합니다.

  • 하위 클래스에서 필수 생성자를 작성할 때는 "override" 키워드를 작성할 필요가 없으며, "required" 키워드만 작성하면 됩니다.

  • 하위 클래스에서 다른 지정 생성자를 구현하지 않는다면, 필수 생성자는 자동 상속됩니다.

상위 클래스와 하위 클래스에 각각 필수 생성자 생성

class AB{
    var a: Int
    var b: Int
  
    required init(a: Int, b: Int){
        self.a = a
        self.b = b
    }
}

class C: AB{
    var c: Int
  
    init(a: Int, b: Int, c: Int){
        self.c = c
        super.init(a: a, b: b)
    }
 
    required init(a: Int, b: Int) {  // override 키워드를 사용할 필요가 없습니다.
        self.c = 0              // 초기화 필수!!
        super.init(a: a, b: a)  // 초기화 필수!!
    }
}


var kim = C(a: 10, b: 10, c: 10)
kim.a    // 10

var lee = C(a: 100, b: 100)
lee.a    // 100

하위 클래스에 필수 생성자를 작성 X

🚨상위 클래스에 필수 생성자가 있는데, 하위 클래스에서 구현을 안 할 경우
'required' modifier must be present on all overrides of a required initializer 와 같은 에러 문구가 나온다.

class AB{
    var a: Int
    var b: Int
  
    required init(a: Int, b: Int){
        self.a = a
        self.b = b
    }
}

class C: AB{
    var c: Int

    init(a: Int, b: Int, c: Int){ //'required' modifier must be present on all overrides of a required initializer
        self.c = c
        super.init(a: a, b: b)
    }

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

📌 2-4. 실패가능 생성자(Failable Initializer)

  • 실패가능 생성자(Failable Initializer)인스턴스를 생성하는 과정에서 실패할 가능성이 있는 생성자입니다.

  • "?" 또는 "!" 키워드를 사용하여 실패가능 생성자를 만들 수 있습니다. (init? / init!)

  • 실패 가능한 생성자로 만들어진 값들은 옵셔널 타입을 가지고 있기 때문에 언래핑 과정을 거쳐야 합니다.

  • 실패가능 생성자는 상황에 따라 호출의 가능 여부가 달라집니다.

✅ 실패가능 생성자의 생성자 호출

실패 가능성이 없는 생성자실패 가능한 생성자호출할 수 없습니다.
실패 가능성이 없는 생성자실패 가능성이 없는 생성자호출할 수 있습니다.

실패 가능한 생성자실패 가능한 생성자호출할 수 있습니다.
실패 가능한 생성자실패 가능성이 없는 생성자호출할 수 있습니다.

⭐️옵셔널 타입은 일반 타입이 가질 수 없는 nil값을 할당할 수 있기 때문에 옵셔널 타입이 더 큰 개념이라고 볼 수 있다. 또한 일반 타입은 옵셔널 타입으로 접근할 수 없다.⭐️

✅ 가장 기본적인 실패가능 생성자 구현

class AB{
    var a: String
    var b: String

    init?(a: String, b: String){
        if a == "" || b.isEmpty{return nil}
        self.a = a
        self.b = b
    }
}

if let kim = AB(a: "에이", b: "비"){   //옵셔널 바인딩을 통한 언래핑
    kim.a   //a
    kim.b   //b
}

✅ 실패 가능한 생성자 호출 (상속X)

  • 실패 가능한 생성자는 실패 가능한 생성자를 호출할 수 있습니다.
  • 실패 가능성이 없는 생성자는 실패 가능성이 없는 생성자를 호출할 수 있습니다.
class AB{
    var a: String
    var b: String
   
    init?(a: String, b: String){
        if a == "" || b.isEmpty{return nil}
        self.a = a
        self.b = b
    }
  
    convenience init?(a: String){   // 호출 가능
        self.init(a: a, b: "아무개")
    }
 
    convenience init(b: String){    // 오류 발생!! 
        self.init(a: "아무개", b: b)
    }
}

✅ 실패 가능한 생성자 호출 (상속O)

class AB{
    var a: String
    var b: String
  
    init?(a: String, b: String){
        if a == "" || b.isEmpty{return nil}
        self.a = a
        self.b = b
    }
}

class C: AB{
    var c: String
  
    init?(a: String, b: String, c: String){
        if a == "" || b.isEmpty{return nil}
        self.c = c
        super.init(a: a, b: b)
    }
}

번외1. 멤버와이즈 생성자(Memberwise Initializer)

구조체는 초기값을 할당하지 않고 바로 사용이 가능하다는 장점을 가지고 있습니다.

구조체는 내부적으로 기본 생성자 init( ) 메서드를 자동으로 생성해주는데 이러한 방법의 생성자를 멤버와이즈 생성자(Memberwise Initializer)라고 부릅니다.

✅ 생성자 작성 없이 바로 사용(내부적으로 생성자가 자동 정의되어 있음)

struct ABC{
    var a: Int
    var b: Int
    var c: Int
}

var kim = ABC(a: 1, b: 2, c: 3)
kim.a   // 1

번외2. 구조체의 편의 생성자?

  • 구조체는 "convenience" 키워드를 작성하지 않고도 내부의 지정 생성자를 호출할 수 있습니다.

  • 이는 구조체는 클래스와 다르게 call by value 타입이기 때문에 가능한 방법입니다.

  • 하지만 해당 방법은 내부의 지정 생성자를 호출할 수 있다고 해서 해당 생성자는 편의 생성자는 아닙니다.

✅ 구조체의 편의 생성자?

struct AB{
    var a: Int
    var b: Int
  
    init(a: Int, b: Int){
        self.a = a
        self.b = b
    }
 
    init(a: Int){    // 구조체는 "convenience" 키워드를 작성할 필요가 없다.
        self.init(a: a, b: 0)
    }
}


3. 소멸자(Deinitialization)

  • 소멸자(Deinitialization)는 인스턴스의 사용을 끝내고, 메모리에서 인스턴스 데이터가 소멸할 때 작동하는 메서드입니다.

  • 소멸자는 클래스에서만 사용 가능한 메서드 입니다.

  • 소멸자는 클래스 내부에 최대 1개만 작성할 수 있습니다.

✅ 소멸자 메서드

class A{
    var a: String
   
    init?(a: String){
        if a.isEmpty{return nil}
        self.a = a
    }
  
    deinit{
        print("인스턴스 소멸")
    }
}

if let kim = A(a: "아무개"){
    print("\(kim.a)")
}

/*
 출력 결과
 아무개
 인스턴스 소멸
*/


Reference

참고자료: 앨런 Swift문법 마스터 스쿨

profile
응애 나 코린이(비트코인X 코딩O)

0개의 댓글