iOS/Swift

Swift) 초기화(Initializers) 이해하기 (5/6) - Required initializer

소들이 2022. 10. 5. 19:56

 

 

 

안녕하세요, 소들입니다 :>

유연 근무제로 인해 공부 시간과 취미 시간을 모두 얻었지만

피곤을 x100배로 얻어버렸다ㅠ;; 망할

그래도 전 요즘 기타 치느라 박인 왼손의 굳은살을 자랑하는 낙으로 산답니다

응 이제 코드 몇 개 치는 게 다야~ 소들갑이야~

사실!!! 포스팅 시간과 공부 시간이 많아진 게 가장 좋네요 XD

카페 값으로 월급 다 나가겠어^^;;

 

Aㅏ.. 초기화 이해하기 시리즈가 5편에서 6편이 된 것 같은 건 피곤한 여러분의 기분 탓입니다ㅎ..

쨌든 이번 포스팅에선 required init에 대해 알아보려고 합니당!!

이번 포스팅은 지금껏 공부한 것보다 크게 어렵지 않은 내용이랍니다~~

모든 포스팅은 편의 말투로 합니다~!!

 

 

 

 

1. required init

 

필수 생성자로, 슈퍼 클래스에서 정의해둘 경우

서브 클래스가 슈퍼 클래스의 생성자를 상속받지 않는 한

서브클래스에서 반드시 구현해주어야 합니다

 

필수 생성자는 말이야.. 말 그대로..ㅋㅋ... 필수 생성자

서브 클래스에서 반드시 구현해야 하는 생성자의 경우,

슈퍼 클래스에서 required init으로 설정해두는 것임

 

사실 개발자가 필수 생성자를 직접 구현할 일은 거의 없기 때문에!

생소할 수 있음!

 

그러나 UIView / UIViewController 를 상속받은 클래스를

xib나 storyboard 없이 코드로 화면을 꽤나 구성해보신 분이라면 바로,

 

 

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
 

 

 

이 코드가 익숙할 것임!!

ㅇㅣ 포스팅에서 required init에 대해 공부한다면,

이 위 코드가 언제 등장하는지에 대해 알 수 있음!!!

 

자, 다음 코드를 봐보자 :)

 

 

class Human {
    var name: String?
    
    required init(name: String) {
        self.name = name
    }
}
 

 

 

위와 같은 코드가 있음!!

Human이란 클래스 안에 required init(name:)이란 필수 생성자를 작성했음

 

아하! 그러면 이제

Human을 상속받는 모든 서브 클래스는 required init(name:)를 필수적으로 작성해야겠네요!?

싶잖음!?

 

 

class SodeulHuman {
    var nickName: String
    
    init(nickName: String) {
        self.nickName = nickName
        super.init(name: "")
    }
    
    //erorr! 'required' initializer 'init(name:)' must be provided by subclass of 'Human'
}
 

 

 

맞음!! 실제로 Human이란 클래스를 상속받는 Sodeul 클래스의 경우,

required initializer를 작성하지 않았다며 에러가 남!!!

 

따라서 다음과 같이

 

 

class SodeulHuman {
    var nickName: String
    
    init(nickName: String) {
        self.nickName = nickName
        super.init(name: "")
    }
    
    required init(name: String) {
        fatalError("init(name:) has not been implemented")
    }
}
 
 

 

 

Human 클래스에서 정의 했던 required init(name:)을 정의해주어야만 에러가 사라짐

아닝 근데 Human클래스의 required init(name:)을 그대로 작성하는 거면

이것은 오버라이딩 아닌가용?? 근데 왜 오버라이딩 키워드를 안 붙이나용??

 

결론적으론 오버라이딩이 맞음!

근데 required init 같은 경우,

override 키워드 없이, 슈퍼 클래스와 동일한 형태로 구현을 해주어야 함!!

 

자, 근데 

 

 

class SodeulHuman {
    var nickName: String?
}
 

 

 

이렇게  모든 프로퍼티가 기본 값을 갖고 있어 지정 생성자를 따로 작성하지 않으면

부모 클래스의 지정 생성자를 모두 상속받는다는 사실저번 포스팅에서 공부 했잖음!?

근데 이렇게 부모의 생성자를 모두 상속받을 경우에는,

required init(name:)을 구현하지 않아도 에러가 안 남!!

 

아항! 

required init은 서브 클래스에서 "지정 생성자를 직접 구현"했을 경우에만 필수적이구나!

 

왜냐?

부모의 모든 생성자를 상속받으면 부모에 선언되어 있는 required init(name:)을 상속 받지만,

자식 클래스에서 지정 생성자를 구현해버릴 경우, 더이상 상속 조건에 맞지 않아

required init(name:)을 상속받을 수 없어서

이땐 required init(name:)를 자식 클래스에서 필수로 정의 해줘야 하는 것임

 

 

 

 

2. NSCoding 네 놈 때문이었다

 

자, 이제 required init을 우린 이해해버렸음!!

그럼 다시 돌아가서,

UIView / UIViewController로 이해를 해보자면,

UIView / UIViewController를 상속받아 커스텀 클래스를 만들 때,

 

 

import UIKit
 
class SodeulViewUIView {
    func draw() { }
}

 

 

이렇게 지정 이니셜라이저를 직접 서브 클래스에서 작성하지 않을 경우엔,

아무런 문제가 없지만,

 

 

import UIKit
 
class SodeulViewUIView {
    override init(frame: CGRect) { ... }

    required init(coder: NSCoder) {
        fatalError("init(name:) has not been implemented")
    }

    func draw() { }
}

 

 

위처럼 지정 생성자를 직접 서브클래스에서 작성할 경우엔,

required init이 필수적이라고 위에서 배웠음

 

이번엔 그 이유를 보자면,

SodeulView의 상위 클래스인 UIView가 바로 

 

 

 

 

NSCoding이란 프로토콜을 채택하고 있음!!!

 

자, 우리가 코드로 화면을 구성할 수도 있지만, storyboard나 xib를 이용해서 View를 구성할 때도 있잖음??

그때 그 storyboard나 xib는 우리가 만들어둔 UI의 형태를 xml 형태로 저장하고 있음!

이 저장된 형태를 우리의 화면으로 가져오기 위해서는 이 xml형태로 저장된 파일의 구성을 가져와야 하는데,

이 구성을 가져올 때 사용되는 것이 바로 init(coder: NSCoder)란 것임!!!

 

 

class SodeulViewUIView {
    
    // 코드로 뷰를 생성할 때를 위한 지정 생성자
    init() {
        super.init(frame: .zero)
    }
    
    // 위 지정 생성자가 생김에 따라 부모 클래스 이니셜라이저 상속이 불가하여 구현해줘야하는 Interface Builder용 생성자
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
 

 

 

따라서 UIView / UIViewController 에서 storyboard나 xib를 통해 뷰를 생성할 때

required init(coder: NSCoder)는 반드시 필요한 것임!!

따라서 저 NSCoding 안에 requred init으로 선언되어 있다고 봤음..(아마도.. ?)

 

근데 xib, storyboard로 화면을 그릴 땐

서브 클래스에서 지정 초기화를 따로 생성할 필요가 없으니 생성하지 않으면,

required init(coder: NSCoder)를 자동으로 상속받아 굳이 서브 클래스에서 구현해줄 필요가 없지만,

 

나는 xib / storyboard 말고 코드로 직접 뷰를 생성하고 싶은데요?! 할 때는

새로 지정 생성자를 구현해야 하고

(이때 새로 구현하건, 오버라이딩 받던 새로 만든 생성자는 당연히 부모의 지정 생성자를 호출 해줘야 함),

새로 지정 생성자를 직접 구현한다는 것 자체가 부모의 생성자를 상속받지 않는단 것이기 때문

이땐 required init(coder: NSCoder)을 직접 구현해주어야 하는 것임

 

근데 required init의 내용이 자동으로 fatal error인 이유는,

 

너 코드로 작성한다며!!!!! 그래서 지정 생성자 만들었자나!!!!!!!!! 

그래서 코드로 이 뷰를 만든다면 xib, stroyboard일 때만 불려야하는 이 생성자는 절대 불리면 안 돼!! 앱 종료!!!!!

 

라는 느낌이지만

 

사실 난 그래서 저렇게 fatal error보단,

 

 

required init?(coder aDecoder: NSCoder) {
    super.init(coder: coder)
}
 

 

 

이렇게 super.init으로 명시해주는 편이긴 함 ..:D

 

 

 

 

.

.

.

required init에 대해 알아봫ㅆ는데용...

음.. 뭔가 설명이 명확하지 않은거 같아서 음.....

나중에 보충... 할...... ㄱ..ㅓ..임...ㅠ..

이해가 안되거나 잘못된 내용이 있으면 반드시 피드백 주세용!!