🍃 출처 : 앨런 swift 문법 마스터 스쿨 수업을 듣고 제가 이해한대로 정리해서 올리는 포스팅입니다.
- 오로지 클래스만 상속이 가능하며, 상속은 단일 상속만 허용한다. (다중상속X)
- 클래스는 메서드, 속성 및 기타 특징들을 상속할 수 있다
✨ 수직확장 : 본질적으로 성격이 비슷한 타입을 새로 만들어 1) 데이터(저장속성)를 추가하거나 2) 기능(메서드)를 변형시켜서 사용하려는 것
예시
// 슈퍼클래스 class Person() { var id = 0 var name = "이름" var email = "abc@email.com" }
⬇️ 상속
// 서브클래스 class Student: Person { // id 변수존재 // name 변수존재 // email 변수존재 var studentId = 0 }
✏️ 상속의 기본 문법
class AClass { var name = "이름" } class BClass: AClass { var id = 0 } let b = BClass() b.id b.name
- 상위 클래스에서 존재하는 멤버를 변형하는것. 즉, 재정의
- BClass는 AClass를 상속해서 구현
- 서브 클래스는 슈퍼클래스로부터 멤버를 상속
- 클래스 상속금지 키워드 : Final
ex) final class AClass {
final var name = "이름"
...
: 서브클래스에서 슈퍼클래스의 동일한 멤버를 변형하여 구현 - 오버로딩 (overloading) : 과적. 함수 하나에 여러가지 함수 대응 - 오버라이딩 (overriding) : 재정의
1) 속성 : 저장속성 재정의 X
2) 메서드 : 메서드, 서브 스크립트, 생성자 ➡️ 재정의 방식이 다름
예시
class Human {
func description() {
print("나는 사람입니다.")
}
}
class Teacher: Human {
// 한 클래스 내에서 동일 메서드를 두 개 선언할 수 없으니 재정의 해줌
override func description() {
print("나는 선생님입니다.")
}
}
let jess = Teacher()
jess.description()
✏️ 재정의 규칙
대원칙
1) 저장 속성의 재정의 불가 (상속가능) / 즉, 변형시켜서 사용할 수 없다
2) 메서드는 제정의 가능 (다만, 기능 확장만 가능)
✏️재정의의 기본 문법
class SomeSuperClass { // 저장 속성 var aValue = 0 // 메서드 func doSomething() { print("Do Something") } } class SomeSubClass: SomeSuperClass { // 저장속성 ➡️ (재정의) 계산속성 override var aValue: Int { get { return 1 } set { super.aValue = newValue } } } override func doSomething() { // 상위 구현 호출 super.doSomething() print("Do Something2") }
var id = 0 var name = "이름" var email = "abc@email.com"
<대원칙>
- ⭐️ 재정의 불가
- 메모리 구조에서 상위 구현을 창조하기 때문에 재정의(변형) 불가
class Human { var name = "Jess" } class Teacher: Human { override var name: String = "Felix" // 🚨컴파일에러 }
<예외>
class Human {
var name = "Jess"
}
class Teacher: Human {
override var name: String {
get {
return self.name
}
set {
self.name = newValue
}
}
}
이렇게 getter / setter를 모두 구현해주면 오버라이딩이 됨
(2) 속성 감시자 형태로 재정의 가능
(실질적 단순 메서드 추가)
: 실질적 메서드
- 확장 방식의 재정의 가능
(1) 읽기 ➡️ 읽기/쓰기 가능 (기능 확장 가능)
(2) 읽기/쓰기 ➡️ 읽기는 불가능 (기는 축소 불가능)- 속성 감시자를 추가하는 재정의 기능 (실질적 단순 메서드 추가)
ㅡ
- 구조체 / 클래스 / (열거형)
- 인스턴스를 생성하는 과정
- 각 "저장속성"에 대한 초기값을 설정하여 인스턴스를 사용가능한 상태로 만드는 것
(열거형은 저장속성이 존재하지 않으므로, case중에 한가지를 선택 및 생성)- 결국, 이니셜라이저의 실행이 완료되었을 때,
인스턴스의 모든 저장속성이 초기값을 가지는 것이 생성자의 역할.class Dog { var name: String var weight: Double } ... // 초기화 메서드 (생성자) init(name: String, weight: Double) { self.name = name self.weight = weight } ... }
✏️ 초기화의 방법(저장속성이 초기값 가져야 함)
1) 저장 속성의 선언과 동시에 값을 저장
2) 저장 속성을 옵셔널로 선언 (초기값이 없어도 nil로 초기화)
3) 생성자에서 값을 초기화
- 반드시 생성자를 정의해야 하는 것은 아님.
이니셜라이저를 구현하지 않아도, 컴파일러는 기본생성자를 자동으로 생성 ➡️ init()
✨ 이니셜라이저 구현하면 기본 생성자를 자동으로 생성하지 않음
✏️ 멤버와이즈 이니셜라이저
-구조체
struct Color1 {
var red: Double =
var green: Double
var blue: Double
}
var color1 = Color1()
color1 = Color1(red: 1.0, green: 1.0, blue: 1.0) // 자동구현
✏️ 초기화 메서드 (생성자)
✨ 오버로딩 지원
- 다양한 파라미터 조합으로 생성자를 여러 개 구현가능
(여러가지 방식으로 인스턴스를 생성하는 방법을 제공하는 것)
✨ 생성자 직접 구현하지 않으면
🚨 (사용자 정의 (직접구현)가 일단 원칙, 개발자 의도가 우선)
(1) 모든 저장 속성에 기본값(또는 옵셔널 타입) 전제
- 클래스 : 기본 생성자 init() 제공
- 구조체 : 멤버와이즈 이니셜라이저 기본 제공
- 구조체 / 클래스 / (열거형)
init(name: String, weight: Double) { self.name = name self.weight = weight
- init(...) 형태를 가지는 생성자 - 지정생성자는 모든 저장 속성을 초기화해야함 (저장속성의 선언과 동시에 값을 저장하거나, 저장 속성을 옵셔널로 선언하는 것도 가능) - 오버로딩이 가능하므로, 다양한 파라미터 조합으로 지정생성자 구현 가능 - (따로 지정하지 않아도) 모든 저장 속성이 초기화 되는 경우, 기본 생성자 자동 제공 ➡️ init() - 생성자를 1개이상 구현하면 기본 생성자를 제공하지 않음
- 클래스
convenience init() { self.init( ... ) }
- 지정생성자보다 적은 갯수의 파라미터로 보다 편리하게 생성하기 위한 서브개념의 생성자
- 편의생성자는 지정생성자에 의존 및 호출 (지정생성자가 저장속성 초기화)
- 초기화 과정을 간편하게 제공하기 위함
- 실질적으로 가능한 지정생성자의 갯수를 줄이고,
편의생성자에서 지정생성자 호출하도록 하는 것이 바람직- 상속했을 때, 편의생성자의 경우 서브클래스에서 재정의를 못함 (하위에서 호출불가가 원칙)
- 편의생성자는 다른 편의생성자를 호출하거나, 지정생성자를 호출해야함 (궁극적으로는 지정생성자를 호출)
✨ 서브클래스의 편의생성자는 자기 단계의 지정생성자를 반드시 호출해야 하고, 그 지정생성자는 슈퍼클래스의(상위의) 지정생성자를 호출해야 한다 ➡️ 이것이 바로 상속관계에서 생성자 위임 규칙 ✏️ 1) 델리게이트 업 : 지정생성자는 슈퍼클래스의 지정생성자를 반드시 호출해야한다 2) 델리게이트 어크로스 : 편의생성자는 궁극적으로 지정생성자호출해야한다 ~ ✔️ 메모리구조 : 서브부터 데이터 저장 (주교재 446)
✏️ 생성자의 상속/재정의
- 지정생성자/편의생성자 상속과 재정의 규칙
* 생성자- 생성자는 기본적으로 상속되지 않고 재정의 원칙
(생성자는 모든 저장 속성을 초기화하는 도구이기 때문에, 서브클래스에 최적화 되어있지 않음)- ⭐️ 원칙: 1)상위 지정생성자와 2)현재 단계의 저장속성을 고려하여 구현
class Dog { var name: String var weight: Double ... required init() { self.name = "강아지" self.weight = 10.0 } ... }
- 클래스의 생성자 앞에 required(필수의) 키워드를 붙이면 하위클래스에서
반드시 해당 생성자를 구현해야 함- 해당 생성자의 의미(파라미터 이름 및 타입이 동일한)
- 하위 클래스에서 필수 생성자 구현 시, overrie(재정의) 키워드 없이 required(필수의)
키워드만 붙이면 됨- ⭐️ 필수생성자 자동상속 조건 : 다른 지정 생성자를 구현 안하면,
자동으로 필수 생성자 상속됨- 애플이 미리 만들어놓은 프레임워크에는 필수 생성자가 있는 경우가 많기에 알고 있어야함!
ex) UIView
class Dog { var name: String ... init?(name: String) { if name.isEmpty { return nil } self.name = name } ... }
- 인스턴스 생성 시, 실패가능성을 가진 생성자
➡️ 실패가 불가능하게 만들어서, 아예 에러가 나고 앱이 완전히 꺼지는 가능성보다는
실패가능 가능성 생성자를 정의하고 그에 맞는 예외 처리를 하는 것이 더 올바른 방법- 인스턴스 생성 실패 시 nil을 리턴한다
- 생성자에 ?를 붙여서, init?(...) 으로 정의
(다만, 오버로딩으로 인한 구분이 안되므로, 해당 이름을 가진 생성자는 유일한 생성자여야 함
실패여부 알 수 없기 때문)1) 동일 단계 / 상속 관계에서의 호출 : 실패불가능 생성자는 다른 실패가능 생성자를 호출 불가능 2) 상속관계에서 재정의 - (상위)init? ➡️ (하위)init (O) _ 강제 언래핑 활용 가능 - (상위)init ➡️ (하위)init (X) - init! 생성자는 init? 과 유사하게 취급하면 됨
class Dog { deinit { print("객체의 소멸") } }
- 생성자와 반대되는 개념
- 메모리가 사라질 때 호출됨, 파라미터도 없음
- 인스턴스 해제 시, 정리가 필요한 내용을 정의
- 클래스에는 최대 1개의 소멸자가 존재
- 인스턴스가 메모리에서 제거되기 직전에 자동으로 호출되는 메서드 부분
- 소멸자는 클래스에만 존재 (힙영역에 저장이 되고, 메모리 관리를 해주어야 하기 때문에)
예시
class Aclass {
var x = 0
var y = 0
deinit {
print("인스턴스의 소멸 시점")
}
}
var a: Aclass? = Aclass()
a = nil // "인스턴스의 소멸 시점"
// 메모리에서 없어질 때 자동 출력 . 실제로 프린트로 많이 구현
// 왜 필요할까? : 특별한 작업을 수행중일 경우 정리가 필요한 상황 등 ..
ㅡ
총정리