Skip to main content

Command Palette

Search for a command to run...

UIViewController의 Life Cycle의 중요성 ( feat. @IBOutlet)

UIViewController는 간단한 객체가 아니다.

Updated
UIViewController의 Life Cycle의 중요성 ( feat. @IBOutlet)
T

안녕하세요🙇🏻‍♂️ 세상을 더 편리하게 바꾸고 싶은 iOS 개발자 최인호입니다.

Hello 👋 I'm Inho Choi, an iOS developer who wants to change the world more conveniently.

  • 대학교 졸업
  • Apple Developer Academy @ POSTECH 1기
  • KWDC Main Organizer
  • AsyncSwift Organizer

룰루랄라 캉테 프랑스 첼시

룰루랄라 신나게 코딩하고 있었다. 화면전환만 연결해주면 되는데

근데! Storyboard로 연결한 객체 UILable, UIImageView 등 모든 IBOutlet 객체가 nil 인 것이다.

액션쾌감!!! 던전앤파이터!

맨붕이 왔다. 왜지? Storyboard에서 UIViewConroller로 전환하는게 실패한 것인가?

아니다. 캐스팅까지 완벽하게 되었는데!

정답은 UIViewController에 대한 내 이해도가 부족했기 때문에 일어난 문제였다.

백지영 그 사람이 바로 나에요 짤 <카톡 프사용> 바로 나예요~ : 네이버 블로그

히히 내가 어떤 점의 이해가 부족했는지 살펴보자


class FirstViewController: UIViewController {
    private let secondVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "SecondViewController") as? SecondViewController
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        print("First VC init] textLabel is nil? \(secondVC?.textLabel == nil)")
    }

    @IBAction func tappedButton(_ sender: Any) {
        guard let secondVC else { return }
        secondVC.textLabel.text = "이제 될 꺼임"
        print("tappedButton] textLabel is nil? \(secondVC.textLabel == nil)")
        navigationController?.pushViewController(secondVC, animated: true)
    }
}



class SecondViewController: UIViewController {
    @IBOutlet weak var textLabel: UILabel!
    var text: String?

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

        print("Second VC init] textLabel is nil? \(textLabel == nil)")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        textLabel.text = text
    }
}

아주 가볍게 그 때의 상황을 재현해 보았다.

Storyboard에서 가운데 ViewController가 FirstViewController 그 오른쪽이 SecondViewController 이다.

버튼을 누르면 SecondViewContoller로 전환해주는 간단한 앱을 만들었다.

위 코드는 실행 될까? 안될까?

버튼을 누르는 순간! 삐빅 에러를 뱉는다.

Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

nil 인데 암시적 unwrapping해서 에러라고 함

짤모음24탄* 아는형님짤/칭찬해짤/카톡짤/웃긴짤/짤방 : 네이버 블로그

ViewController가 정상적으로 객체로 만들어졌는데 왜? 안될까?

정답은 ViewController의 생명주기를 무시했기 때문이다.

간단한 생명주기로 loadView -> viewDidload -> viewWillAppear ... 이 있다.

괜히 이러한 생명주기가 있는게 아니다.

내가 무시한건 저 load 키워드와 관련된 생명주기다.

load : 적재하다.

객체가 만들어지면 안에 있는 IBOutlet 만들어 질까? -> X

View 계층에 올려지면 IBOutlet 만들어 진다. -> O

그럼 다시 에러를 보자.

난 navigation에 Push되기 전에 Label에 접근했다.

즉, 컴포넌트들이 load 되지 않은 상태에 접근했기에 nil 인 값이었던 것이고 에러가 난 것이다.

그럼 어떻게 전달해야 할까?

import UIKit

class FirstViewController: UIViewController {
    private let secondVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "SecondViewController") as? SecondViewController
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        print("First VC init] textLabel is nil? \(secondVC?.textLabel == nil)")
    }

    @IBAction func tappedButton(_ sender: Any) {
        guard let secondVC else { return }
        secondVC.text = "이제 될 꺼임"
        print("tappedButton] textLabel is nil? \(secondVC.textLabel == nil)")
        navigationController?.pushViewController(secondVC, animated: true)
    }
}



class SecondViewController: UIViewController {
    @IBOutlet weak var textLabel: UILabel!
    var text: String?

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

        print("Second VC init] textLabel is nil? \(textLabel == nil)")
    }

    override func loadView() {
        print("SecondVC loadView-super.loadView() before] textLabel is nil? \(textLabel == nil)")
        super.loadView()
        print("SecondVC loadView-super.loadView() after] textLabel is nil? \(textLabel == nil)")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        textLabel.text = text
    }
}

오 이제 잘 된다. 왜냐하면 load된 이후에 Label에 텍스트를 지정해 주었기 때문이다.

UIViewConroller를 간단한 Class로 생각하지 마라!

화면을 구성하는 복잡한 아이이다. 괜히 면접 단골로 View Life Cycle을 물어보는게 아니다.

UIViewController에 문제가 생겼다면 혹시 내가 Life Cycle을 무시하는게 아닌지 생각해보자!

그럼 출력 값을 볼까?

super.loadView()과정 이후에 textLabel 객체가 생성됨을 알 수 있다.

즉! UIViewController가 loadView를 마쳐야 IBOutlet의 객체들이 생성되는 것이다!

💡
UIViewController와 IBOutlet 객체 생성시기를 잘 생각하자!
💡
ViewConroller를 간단한 Class로 생각하지 마라!

+ 추가로 이렇게 구성해도 됨

import UIKit

class FirstViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func tappedButton(_ sender: Any) {
        if let vc =  UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "SecondViewController") as? SecondViewController {
            vc.completeHandler = {
                vc.textLabel.text = "이제 될꺼임"
            }
            navigationController?.pushViewController(vc, animated: true)
        }
    }
}

class SecondViewController: UIViewController {
    @IBOutlet weak var textLabel: UILabel!
    var completeHandler: (() -> Void)?

    override func viewDidLoad() {
        super.viewDidLoad()

        completeHandler?()
    }
}

More from this blog

[CS] Https는 대칭키일까? 비대칭키일까?

결론: 둘 다 쓴다. 하지만 쓰는 타이밍은 다르다. 자세하기 알아보자. 혹시 모르니 용어는 정리해두자. 암호화 <-> 복호화 해독 = 복호화 비대칭키 해독은 대칭키 해독보다 오래 걸린다. 비대칭키를 쓰는 과정 💡 비대칭키는 대칭키를 만들기 위한 여정에 쓰인다. 뭔 소리지??? 대칭키를 만들기 위해서 비대칭키가 쓰인다고?? 비대칭키 해독은 대칭키 해독보다 오래걸린다. 1. Client가 Server에 접속을 요청하면 서버는 Secret ...

Jun 6, 202417

Xcode Cloud 사용 후기

애플에게 내는 친구비를 내며 Apple Developer 계정을 유지하려면 매년 애플에게 13만원의 친구비를 지불해야한다. 그리고 몇 개 친구 답례 상품을 주는데 그 중 하나가 Xcode Cloud 상품 주는데 안 쓸 이유는 없지 Xcode Cloud 개인에게는 과연? K-Spam 이라는 앱을 오랫동안 구상하고 드디어 출시를 했다. 생각보다 삽질을 많이 한 것 같다. 하지만 사이드 프로젝트의 진정한 의미는 삽질(?)하며 배우는 것이 아니겠나 개...

Jun 3, 202418

iOS Version 주의

앱 버전에는 상위버전이 출시한 상태에서 하위 버전을 아카이브 할 수 없음 예를 들어 출시한 버전이 1.3 이면 1.2로는 아카이브가 불가능함 단, 출시한 적이 없는 앱이라면 버전은 마음대로 지정 가능 App Store Connect의 버전 예를 들어 1.03 이랑 1.1 이랑 비교하면 앱 버전은 1.03 > 1.1 로 인식한다. . 단위로 잘라서 Int로 인식하는 것 같다. 그러니 0은 최대한 사용하지 않고 구분한다면 . 으로 구분해야겠다.

Jun 1, 20248

Toby의 iOS 블로그

34 posts

안녕하세요 세상을 더 편리하게 바꾸고 싶은 iOS 개발자입니다.