Starbucks Caramel Frappuccino
본문 바로가기
  • 그래 그렇게 조금씩
UIKit/UIKit

init(frame:) & init(coder:)

by Toughie 2023. 4. 11.

커스텀 뷰(UIView를 상속)를 만들다 보니 아래와 같은 생성자들을 만났다.

    override init(frame: CGRect) {
        super.init(frame: frame)
    }
required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
}

보통 아래 코드는 컴파일러 fix를 통해 그냥 추가했었는데..

어떤 생성자들이고 왜 꼭 구현해야 하는지 궁금해져서 알아보았다.

 

먼저 

init(frame:)

https://developer.apple.com/documentation/uikit/uiview/1622488-init

 

init(frame:) | Apple Developer Documentation

Creates a view with the specified frame rectangle.

developer.apple.com

Discussion 파트를 읽어보면..

새로운 뷰 객체는 사용되기 전에 window의 뷰 계층에 삽입되어야 한다.

만약 코드로 뷰를 만들면(스토리보드, nib 등의 InterFace builder가 아닌) 이 메서드는 UIView Class의 지정 생성자이다.

서브클래스들은 이 메서드를 오버라이드해서 커스텀을 할 수 있지만, 항상 구현초반부에 super를 호출해야한다.

super.init(frame: frame)

 

만약 인터페이스 빌더(닙파일등)를 이용해서 뷰를 설계하면 순차적으로 객체들이 로드되면서 이 메서드는 호출되지 않는다.

닙파일의 객체들은 init(coder:)를 통해 초기화되고 이 생성자는 닙파일에 저장된 속성들과 뷰의 속성과 맞추는 역할을 한다.

 

파라미터인 frame
포인트로 측정된 (픽셀과 포인트는 다르다) 뷰를 위한 프레임 직사각형.

프레임의 원점은 프레임을 추가하려는 슈퍼뷰에 상대적이다.

이 메서드는 Frame Rectangle을 통해 구현하려는 뷰의 중심과 경계선을 지정한다.

 

-> 쉽게 말하면 인터페이스 빌더가 아닌, 코드로 뷰를 만들 때는 frame rectangle을 이용하기 때문에

UIView를 상속하는 코드 안에서 꼭 호출해야 하는 생성자라는 것이다.

 

init(coder:)

https://developer.apple.com/documentation/foundation/nscoding/1416145-init

 

init(coder:) | Apple Developer Documentation

Returns an object initialized from data in a given unarchiver.

developer.apple.com

우선 Required. 즉 필수 생성자이다. (그래서 컴파일러에서 추가하라고 에러가 뜨는 것이다.)

그리고 unarchiver라는 개념이 있다.

 

코드로 하나하나 속성을 추가/변경하는 것이 아니라

스토리보드 등 인터페이스빌더를 이용하면 클릭만으로 손쉽게 속성을 수정할 수가 있었다.

이게 어떻게 가능할까? 이것을 가능하게 하는 과정이 unarchiving이다.

 

우리가 코드를 작성하면 컴파일러가 컴퓨터가 알아들을 수 있도록 기계어로 변환하는 것처럼,

인터페이스 빌더는 코드가 아니라서 앱을 빌드하고 컴파일하는 시점에서 컴파일러가 인식할 수 있도록
코드로 변환하는 과정이 unarchaiving인 것이다.

(파라미터를 보면 unarchiver object인 decoder가 있다. )

 

NSCoder 타입의 객체를 파라미터로 전달되고

decoding 되어 초기화된 데이터를 반환한다. (self)

즉 컴파일러가 인식할 수 있는 형태로 변환된 자신을 리턴!

 

NSCoding 프로토콜을 이용하면

View의 상태를 앱의 disk에 저장하는 serialize,

앱의 disk에서 View의 상태를 불러오는 deserialize 작업이 가능하다.

 

인터페이스 빌더를 통해서 뷰의 상태(폰트, 컬러 등)를 수정하는 경우

Xcode에서 init(coder:)를 호출해서 serialization, deserializaiton 작업을 해서 변경사항이 적용되도록 한다는 얘기다.

 

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

예시코드를 살펴보면 init뒤에 물음표가 붙어있다. 즉 실패가능 생성자라는 의미다.

 

UIView 클래스는 NSCoder 프로토콜을 채택한다.

NSCoder 프로토콜은 인코딩,디코딩을 지원하는 프로토콜로 UIView는 NSCoder 프로토콜을 통해

뷰 계층 구조의 인코딩,디코딩을 수행하는 것이다. 즉 UIView 객체를 저장/로드한다는 의미다. 

 

UIView 클래스가 NSCoder를 채택했기 때문에 준수하기 위해 꼭 구현을 해야 하고, 그래서 required 즉 필수 생성자가 되는 것이다.

그래서 비록 인터페이스 빌더는 사용하지 않았지만, 커스텀뷰의 클래스가 UIView를 상속하기 때문에 해당 생성자를 추가하라고

컴파일러가 소리를 질렀던 것이다 ㅎㅎ

 

 

궁금증이 해결되었다. 😀

 

 

참고자료
https://leeari95.tistory.com/63

https://velog.io/@inwoodev/iOS-initframe-initcoder