예거's Bicycle for the mind

[iOS] ViewController 클래스의 인스턴스는 어디서 어떻게 만들어질까? 본문

iOS & Swift

[iOS] ViewController 클래스의 인스턴스는 어디서 어떻게 만들어질까?

유예거 2021. 11. 7. 20:46

직전 글에서 UIView 를 다뤘으니, 이번 글에선 UIViewController 의 이니셜라이저에 대해 공부해보려고 합니다.

 

UIViewController 클래스가 가진 2개의 이니셜라이저에 대해 알아보자

애플이 만들어둔 UIKit 내의 UIViewController 클래스 정의부에서 이니셜라이저와 생명주기 관련 메서드만 요약해봤어요.

 

open class UIViewController: UIResponder, NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer, UIFocusEnvironment {

    public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)

    public init?(coder: NSCoder)

    open var view: UIView!

    open func loadView()

    open func viewDidLoad()

    open func viewWillAppear(_ animated: Bool)

    open func viewDidAppear(_ animated: Bool)

    open func viewWillDisappear(_ animated: Bool)

    open func viewDidDisappear(_ animated: Bool)
}

 

정의된 순서대로 하나씩 개념을 잡아봅시다.

 

먼저, UIViewController 클래스 또한 UIResponder 라는 클래스를 상속받고 있으며, 5개의 프로토콜을 준수하고 있어요. (NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer, UIFocusEnvironment)

 

위 개념에 대해선 나중에 다루기로 하고, 이니셜라이저로 가보죠.

 

이니셜라이저가 2개가 있어요.

둘 다 public 접근제어가 걸려있어서, UIKit 모듈 외부에서는 override 할 수 없습니다.

 

중요한 점은, UIViewController 클래스를 상속받았다면, 그 안에서는 이니셜라이저의 override 또한 가능하다는 점입니다.

(글을 마무리하기 전에 실험해보자.)

 

다시 이니셜라이저를 보면, 파라미터도 다르고, 하나는 init? 으로 실패 가능한 이니셜라이저(failable initializer)로 구현이 되어 있어요.

 

여기서 생각이 복잡해집니다.

이니셜라이저는 인스턴스를 만들기 위해 존재하는 것인데, 뷰컨의 인스턴스는 누가 만드는 거지...? 🤔

 

저는 스토리보드에서 view 위에 UI 컴포넌트 들을 올리고 뷰컨 끼리 segue 연결을 해준 적은 있어도, 뷰컨의 인스턴스를 직접 코드에 만들진 않았거든요. 😅

 

이 질문에 대한 힌트는 역시나 UIViewController 의 공식 문서에서 찾을 수 있었습니다.

 

 

(* specify 의 정확한 뜻은 "identify clearly and definitely" 인데, 번역은 "지정하다" 로 처리했어요.)

 

내용을 간단하게 요약해보면, 스토리보드에서 뷰컨과 뷰컨이 가진 view 객체들을 지정하고, 뷰컨들 끼리의 관계와 segue 지정도 해준다고 합니다. (너무나도 맞는 말이죠.)

 

우리가 찾는 부분은 그 아래에 있는데, 스토리보드에서 뷰컨을 로드(load)하기 위해, 해당 UIStoryboard 객체에서 instantiateViewController 메서드를 호출해서 뷰컨의 인스턴스를 생성해준다고 합니다.

 

음? 결국 뷰컨의 인스턴스는 뷰컨이 만드는 게 아니라, 스토리보드가 특정 메서드를 통해 만들어내는 걸까요?

다소 긴 이름을 가진 위 메서드를 조금 더 조사해봅시다.

 

 

[Discussion] 부분을 읽어보면, instantiateViewController 메서드를 호출할 때마다, 뷰컨의 인스턴스를 init(coder:) 메서드를 통해 만들어준다고 써있어요.

 

⭐️ 정리해보자면, 스토리보드가 뷰컨의 인스턴스를 만들어내기 위해 instantiateViewController 메서드를 호출하고, 이 메서드는 다시 UIViewController 의 이니셜라이저 2개 중 하나인 init(coder:) 를 호출하는 거로 이해할 수 있습니다.

 

그렇다면 다른 이니셜라이저는 어떻게 사용될까요? 다시 공식 문서를 살펴보죠.

 

 

다른 이니셜라이저는 Nib 파일을 통해 뷰컨의 인스턴스를 만드는 것입니다.

 

nib 하고 xib 이란 단어는 앞으로 계속 나올 것 같아서 미리 정리하는 게 좋겠어요!

일단, nibNeXT Interface Builder, xibXcode Interface Builder 의 약자입니다.

 

Nib file 공식 문서를 보고, 간단하게 개념을 요약해보자면

⭐️ nib file 은 iOS 앱의 인터페이스 정보(뷰컨, UI 요소들이 포함된 view 객체 등)를 담은 특별한 파일입니다.

 

스토리보드와 같은 인터페이스 빌더에서 만들어진 파일은 XML 문법(Extensible Markup Language)에 맞게 저장된 .xib 확장자를 가지고 있지만, 컴파일 하면 .nib 이 된다고 합니다. 참고로, nib 파일은 바이너리(binary) 파일이래요.

 

위 내용은 이 문서에서 확인할 수 있습니다. 원문은 아래 첨부합니다.

 

The contents of .xib and .storyboard files are stored by Xcode in XML format.
At build time, Xcode compiles your .xib and .storyboard files into binary files known as nibs.
At runtime, nibs are loaded and instantiated to create new views.

 

UIViewController 클래스의 2개의 이니셜라이저 차이점 정리

// nib file 을 통해 뷰컨의 인스턴스를 생성할 때 사용되는 이니셜라이저
public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)

// 스토리보드를 통해 뷰컨의 인스턴스를 생성할 때 사용되는 이니셜라이저
public init?(coder: NSCoder)

뷰컨 이니셜라이저도 override 가 될까?

결론부터 말하면, 됩니다!

 

위에서, UIViewController 클래스의 2가지 이니셜라이저는 public 접근제어가 걸려있는 걸 확인했어요.

public 접근제어 수준은 UIKit 모듈 내부에서만 상속이나 override 가 가능한 건데, UIViewController 를 상속받은 뷰컨이라면, 이미 모듈 내부에 들어와 있다고 볼 수 있어요.

 

아래 예시 코드를 살펴봅시다.

 

// 이니셜라이저 override 실험
class ExampleViewController: UIViewController {
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        print("스토리보드를 통해 Example 뷰컨의 인스턴스가 생성되었습니다.")
    }
    
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        print("nib file 을 통해 Example 뷰컨의 인스턴스가 생성되었습니다.")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print("--Example view did load!--")
    }
}

 

스토리보드를 통해 뷰컨을 지정해주고, 위와 같이 2가지 이니셜라이저를 override 해서 사용해봤어요.

콘솔에 뜬 메시지를 확인해보니, 시뮬레이터가 해당 화면으로 진입할 때, init(coder:) 를 통해 뷰컨의 인스턴스가 생성되는 걸 확인할 수 있었습니다.

 

참고 링크

[애플 공식 문서] UIViewController

[애플 공식 문서] instantiateViewController(withIdentifier:)

[애플 공식 문서] Nib file

[애플 공식 문서] Using Interface Builder

[군옥수수수 블로그] UIViewController Lifecycle

[Zedd 블로그] Access Control(접근 제어) - (1)

[Zedd 블로그] nib과 xib의 차이

[달망이 블로그] iOS의 화면을 그리기 위해선 이것이 필요합니다! XIB, NIB, Storyboard

Comments