UIView와 UIViewController에서 designated Initializer를 정의하게 되면, 

아래와 같은 에러를 보게된다. 

 

'required' initializer 'init(coder:)' must be provided by subclass of 'UIView'

 

 

이 required init?(coder:)에 대해서 알아보도록 하자!

 

에러가 나는 이유

UIViewController와 UIView는 NSCoding이라는 protocol을 채택하고 있는데, 

 

NSCoding protocol은 아래의 method를 구현하도록 한다. 

func encode(with coder: NSCoder) {
  
}
    
required init?(coder: NSCoder) {
     
}

저번 포스팅에서 말했듯이, required 키워드는 subclass에게 오버라이드를 요구한다.

따라서, NSCoding의 subclass인 UIView와 ViewController는 해당 initializer를 오버라이드 해야한다.

 

그렇다면, 왜 init()을 직접 구현하지 않으면 에러가 나지 않는것일까? 

이에 대해 알기 위해 클래스의 initializer 상속 조건을 알아야 한다. 

 

우선, 

class 내의 initializer가 구현되어있지 않을때, (모든 저장 프로퍼티가 초기화 되어있어야만 가능)

컴파일러에 의해 default initializer를 제공받는다. 

해당 경우에는 모든(required, convenience, designated) initializer가 상속받는다.

따라서, required init?(coder: NSCoder) 를 superclass로 부터 상속 받았기 때문에

에러가 나지 않았던 것이다! 

 

반면, 구현을 하게 되면 더이상 상속받지 못하므로 에러가 나며 오버라이드를 해야한다.

이해가 안된다면 저번 포스팅에서 해당 주제에 대해 상세하게 다뤘으니 보길 바란다.

 

 

NSCoding 

이제는 해당 initializer는 왜 필요한지 더 자세히 살펴보도록하겠다. 

앞서 살펴보았을때, NSCoding 포로토콜은 아래의 2 메서드의 구현을 요구하였다.

func encode(with coder: NSCoder) {
  
}
    
required init?(coder: NSCoder) {
     
}

또한 내부에 비슷하게 생긴 NSCoder라는 클래스를 볼수 있는데 이 2가지에 대해 간략히 살펴보자면, 

  • NSCoder: Object가 Archiving & Distribution을 할 수 있도록 해주는 abstract Class
  • NSCoding: Object가 encode, decode 될 수 있도록 만드는 프로토콜

우선, NSCoder는 Abstract Class로, Sub Class로는

NSArchiver, NSUnarchiver, NSKeyedArchiver, NSKeyedUnarchiver, NSPortCoder 가 있다. 

이들을 간단하게 "coder"라고 부르며, 

decode만 가능한 coder를 decoder

encode만 가능한 coder를 encoder라 부른다.

 

NSCoding은 encode, decode가 될수 있도록 만드는 protocol이라고 하였는데, 

encode, decode가 가능해야만, Archiving, Distribution이 가능해진다.

즉!! encode, decode가 될 수있도록 하여, Archiving, Distribution이 가능한 상태로 만드는 protocolNSCoding이고, 

NSCoder의 subclass들이 NSCoding을 채택한 Object를 Archiving, Distribution한다.

 

그렇다면, Archiving과 Distribution은 도대체 무엇일까?

Archiving이란, Object와 value를 아키택쳐와 independent한 stream of bytes(바이트의 열)로 변환 해준다. 

이때, Object와 value사이의 관계의 identity를 보존하면서 변환한다.

바이트의 열로 변환 하는 이유는 파일에 저장하거나, 네트워크 통신을 위해서이다.

변환해주는 애들이 Archiver이고, 이들은 NSCoder의 sub class이다. (NSKeyedArchiver 등이 있다.)

 

Distribution이란, object와 data item다른 process나, threads에 복사해 주는것을 의미한다.

 

 

 

이제 다시 required init?(coder: NSCoder) 에 대해 알아보자! 

우리는 앱의 UI를 code로도 작성할 수 있지만, storyboard와 Xib파일을 통해 작성 할 수 있다. 

storyboard와 Xib파일을 통해 작성된 UINib이라는 바이트 파일로 저장이 되는데

즉, storyboard와 Xib파일을 통해 작성된 UI는 Nib이라는 파일에 Archiving 되어 저장된다.

 

하지만, 이를 보여주기 위해서는 Nib에 있는 파일을 불러와야 하는데,

즉, UnArchiving이 진행되어야 한다.

 

required init?(coder: NSCoder) {

}

해당 함수에서 파라미터인 coder는 unArchiving을 진행하는 decoder가 되고,

coder를 통해 nib 파일을 unArchiving을 하여, 초기화를 진행한다! 

 

 

 

반면, 

func encode(with coder: NSCoder) {

}

해당 함수는 required init?(coder:)와 반대의 작업이 진행된다. 

여기서 coder는 encoder가 된다.

 

init(frame:)

class CustomView: UIView {
    
    override init(frame: CGRect) {

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

UIView 를 생성 하면 위와 같이 2가지 init을 볼수가 있는데, 

아까 살펴보았듯이,

required init?(coder: NSCoder)은 StoryBoard나 Xib파일을 통해 작성한 UI를 불러오는 것이였다. 

 

init(frame: CGRect) 은 View instance를 코드로 만들기 위한 initializer이다.

Reference

복사했습니다!