본문 바로가기
개발/iOS

[iOS/Swift] 초기화와 상속

by 마자용 2022. 5. 2.

Swift에서의 초기화

종류: designed init, convenience init (지정 초기화, 편의 초기화)

단계

  • 모든 member 초기화 → 상속 받은 member 커스텀

 

 

지정 초기자

init(parameters) {
	statements
}

클래스 내의 모든 프로퍼티를 초기화한다.

클래스마다 필수적으로 하나씩 가진다.

 

 

편의 초기자

convenience init(parameters) {
		self.init() // ✅
	statements
}

Optional 이다.

내부에 반드시 지정 초기자를 호출해야 한다. (위 코드의 self.init() 부분을 말한다.)

 

 

초기화 규칙

1. 모든 멤버의 초기화를 보장하자 !

  • 지정 초기자 ⇒ 반드시 super class의 지정 초기자를 호출해야 한다. (= delegation)
  • 편의 초기자 ⇒ 반드시 같은 class의 지정 초기자를 호출해야 한다. 모든 member의 초기화를 보장해야 하기 때문이다.

2. 상속에서의 초기화 규칙 == 컴파일을 강제한다 !

  • 자신의 클래스 member들을 모두 초기화하고,
  • super class로부터 상속 받은 member들을 super class의 지정 초기자를 호출함으로써 모두 초기화한다. 

즉 super class의 지정 초기자 호출 전에 자신의 member들과 super class로부터 상속 받은 멤버들을 먼저 초기화 한다는 것이다.

  • 이렇게 하지 않으면 super class의 값들로 덮어 씌워지게 되기 때문이다.
  • 예시 (코드 출처 @seungchan2)
// MARK: - Initialization
init(frame: CGRect, mode: ButtonMode, text: String, fontSize: CGFloat) {
        self.mode = mode
        self.text = text
        
        super.init(frame: frame) // ✅
        
        setUI(text: text, fontSize: Int(fontSize))
        setupMode(mode: mode)
}
    
@available(*, unavailable)
required init?(coder: NSCoder) {
	fatalError()
}

 

 

초기화 원리

모든 멤버들을 먼저 초기화 한 뒤, 커스텀 한다.

  • 등산하고 하산하는 것과 같은 흐름이다.

 

1) 등산

  1. 클래스의 init 호출
  2. 클래스의 인스턴스들에게 메모리 주소를 할당
  3. sub class의 멤버들을 초기화 (= 지정 초기자 호출)
  4. super class의 멤버들을 초기화 (= 지정 초기자 호출)
  • 이를 Root class까지 실행하며 모든 멤버들을 초기화한다.

 

2) 하산

  • super class의 지정 초기자를 호출한 부분에서, 상속 받은 멤버들을 커스텀한다.
  • 1번과 반대로 Root class로부터 시작해서 sub class까지 실행한다.

 

 

초기화 자동 상속

모든 member들을 초기화해야 하므로 생기는 제약사항을 말한다.

  • 모든 신규 멤버들에 default 값이 있으면 자동 상속된다.
  • sub class가 편의 초기자를 하나도 정의하지 않았다면 자동 상속된다. (하나라도 존재한다면 super class의 init을 전부 상속할 수는 없다.)
  • super class의 지정 초기자를 모두 상속 받거나 작성하게 되면, 편의 초기자도 자동으로 상속된다.

 

 

required init

required 키워드를 초기자 앞에 붙이면, 모든 sub class들이 명시적으로 해당 초기화를 구현한다.

  • 이미 상속의 의미가 존재하므로 override는 붙여주지 않는다.

 

 

정리 (예시 코드)

표시해놓은 시간 태그 부분을 커스텀 뷰로 만들면서 전부 사용했다.

import UIKit

import SnapKit
import Then

final class TimeView: BaseView {

    // MARK: - Properties
    let timeLabel = UILabel().then {
        $0.font = UIFont.font(.pretendardMedium, ofSize: 18)
        $0.textColor = Color.gray800
    }
    
    // MARK: - Initialization
    convenience init(time: String) {
        self.init(frame: .zero) // ✅ 같은 클래스의 지정 초기자를 호출
        self.timeLabel.text = time
        updateUI()
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame) // ✅ super class의 지정 초기자를 호출
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
		/*
		✅  모든 sub class들에 명시적으로 지정 초기자 구현
		super class의 초기자를 자동으로 상속받도록 하기 위함
		*/
        fatalError()
    }
    
    // MARK: - Functions
    override func setupView() {
        super.setupView()

        backgroundColor = Color.white
        makeRoundedWithBorder(radius: 6, color: Color.darkMint.cgColor)
        
        addSubview(timeLabel)
    }
    
    override func setupConstraints() {
        super.setupConstraints()
        
        timeLabel.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(6)
            make.leading.equalToSuperview().offset(10)
            make.trailing.equalToSuperview().offset(-10)
        }
    }
    
    private func updateUI() {
        let viewSize = timeLabel.intrinsicContentSize
        let width = viewSize.width + 10
        let height = viewSize.height + 6
        
        frame.size = CGSize(width: width, height: height)
        timeLabel.center = CGPoint(x: width / 2, y: height / 2)
    }
}

 


 

참고

[iOS - swift] Designated init, Convenience init, 초기화의 핵심, 초기화 상속
required init?(coder:)란 무엇인가?

댓글