본문 바로가기
FRONT-END/iOS

클래스의 상속, 초기화, 생성자

by 랄라J 2023. 7. 31.

상속의 의미

  • 성격이 비슷한 타입을 새로 만들어 데이터를 추가하거나 기능을 추가 및 변형해 사용하는 것
  • 상속하는 문법 : class 새로운 클래스명 : 상속하고자 하는 클래스명 {}
  • 상위 클래스에서 존재하는 멤버를 변형하는 것은 재정의라고 함, override 키워드 사용해야함
  • Swift에서는 다중 상속은 불가능
  • class 선언 시 앞에 final 키워드를 붙이면 상속이 불가능하게 만들 수 있음 
  • 변수 앞에 final 키워드를 붙이면 재정의 불가의 의미 

 

클래스 용어 정리

  • 기본(Base) 클래스 : 다른 어떤 것도 상속받지 않은 클래스
  • 부모(Parent) 클래스 | 슈퍼 클래스 | 상위클래스
  • 자식(Child) 클래스 | 서브 클래스 | 하위 클래스 
  • 재정의 : 상위 클래스의 변수를 변형하는 것을 의미함, 저장 속성은 재정의 불가

 

재정의 (오버라이딩, overriding)

  • 클래스의 상속에서 속성/메서드를 재정의해서 사용하는 것을 의미
  • 저장 속성에 대한 재정의는 어떠한 경우도 불가능함이 원칙
    • 상위 저장속성을 하위 클래스에서 읽기 쓰기가 가능한 계산 속성으로 재정의하는 것은 가능
    • 속성 감시자 형태로 재정의하는 것은 가능
  • 계산 속성에 대한 재정의도 기능을 축소한 형태로의 재정의 불가능 (ex. 읽기/쓰기 -> 읽기)
  • 서브클래스에서 부모클래스에 대한 메서드를 재정의할 때는 override라는 키워드를 적고 선언해야함 
class PClass {
	func doFunc () {
    	print("hi hello")
    }
}

class CClass:PClass {
	override func doFunc () {
    	// super.doFunc() <- PClass doFunc도 사용하고 싶을 때
    	print("hi bye")
    }
}

 

 

초기화의 과정과 생성자

  • 저장 속성의 기본값이 모두 선언되어있거나, 옵셔널로 선언하면 클래스 내 init() 함수 없이 즉, 생성자 없이 자동 구현이 됨
  • 생성자는 오버로딩이 가능함
    • 오버로딩 : 하나의 함수 이름에 여러 함수를 대응시켜 사용하는 것
  • 멤버와이즈 이니셜 라이저 : 구조체의 특별한 생성자
    • 구조체는 저장속성에 초깃값을 선언하지 않은 경우에도 호출 시 자동으로 기본값을 적는 코드가 생성되는 것을 의미 
    • 클래스와 달리 저장속성에 초깃값을 선언하지 않아도 내부적 구현으로 init() 생성자가 구현이 됨
    • 단, 개발자가 직접 생성자를 구현하는 경우에는 자동으로 생성자가 구현되진 않음

 

지정생성자 (Designated)

  • init()이 붙은 것은 기본생성자 또는 지정생성자라고 부름
  • 하나의 정석적인 생성자를 만들고, 다른 생성자는 앞서 만든 정식생성자를 호출해 구현하는 방식
  • 생성자 코드가 중복되지 않기 때문에 추후 유지보수에도 좋아 조금 더 올바른 방식으로 볼 수 있음
struct AStruct {
    var name: String
    var weight: Double
    
    init(name: String) {
    	self.init(name: name, weight: 0.0)
    }

    init(name: String, weight: Double) {
    	self.name = name
        self.weight = weight
    }
}

 

편의생성자 (convenience)

  • 클래스에만 존재하는 용어
  • 다른 생성자를 호출하는 생성자는 앞에 반드시 convenience 키워드를 붙여줘야 함
  • 편의생성자는 반드시 다른 편의생성자를 호출하거나 지정생성자를 호출해야함
  • 구조체에는 편의생성자라는 용어가 없음, 클래스에 convenience 키워드를 붙이는 방식으로 사용되는 형태도 지정생성자라고 함
class AClass {
    var name: String
    var weight: Double

    init(name: String, weight: Double) {
    	self.name = name
        self.weight = weight
    }
    
    convenience init(name: String) {
    	self.init(name: "rarla", weight: 0.0)
    }
}

 

클래스에서 더 알아야 할 지정생성자와 편의생성자 관련 지식

  • 지정생성자는 상속 시 반드시 재정의를 해줘야 함
  • 편의생성자는 상속이 되지 않음
  • 델리게이트 업 (Delegate up) : 서브클래스의 지정생성자는 반드시 상위클래스의 지정생성자를 호출해야 함
  • 델리게이트 어크로스 (Delegate across) :  편의생성자는 다른 편의생성자나 지정생성자를 호출해야하고 궁극적으로는 지정생성자를 호출해야함
class AClass {
    var name: String
    
    init(name: String) {
    	self.name = name
    }
}

class BClass: AClass {
    var weight: Double
    
    // 1. 상위클래스의 이름과 같으면 init 앞에 override 키워드를 붙여야 함
    init(name: String, weight: Double) {
    	self.weight = weight
    	super.init(name: name)
        // self.name = "rarla" <- 커스텀 단계, 선택 사항
    }
}

 

필수생성자 (Required)

  • 클래스에서만 있는 개념
  • 생성자 앞에 required 키워드를 붙여 생성자를 생성하면 상속받는 하위클래스에서도 반드시 해당 생성자를 구현해야함
  • 추가로 하위클래스에서 required가 붙은 생성자를 구현 시 override 키워드를 사용할 필요없이 required 키워드만 붙이면 됨
  • 단, 하위클래스에서 다른 지정생성자를 구현하지 않는 경우 swift 내부에서 자동으로 상속되게 되어있음
  • 참고, UIView 등에서 필수생성자로 인한 오류를 마주하는 경우가 있음

 

실패가능생성자 (Failable)

  • 인스턴스 생성에 실패할 수 있는 가능성을 가진 생성자
  • 생성자에 ?를 붙여 init?()로 정의함 / 또는 !를 붙여 init!()로 정의
  • 단, 동일한 파라미터를 가진 생성자는 유일해야함
  • 실패불가능생성자는 다른 실패가능생성를 호출 불가능함

 

소멸자 (Deinitialers)

  • 인스턴스가 메모리에서 해제되기 직전에 자동으로 호출되는 메서드
  • 클래스 정의 시 클래스에는 최대 1개의 소멸자만 정의 가능하며 소멸자는 파라미터를 사용하지 않음
  • deinit 키워드를 사용해 deinit{}으로 선언
  • 상위클래스 소멸자는 하위클래스에 의해 상속됨
  • 상위클래스 소멸자는 하위클래스 소멸자의 구현이 끝날 때 자동으로 호출됨
반응형

댓글