🍃 출처 : 앨런 swift 문법 마스터 스쿨 수업을 듣고 제가 이해한대로 정리해서 올리는 포스팅입니다.
- 상속의 방향과 프로토콜의 필요성
✏️ 클래스와 상속의 단점
1. 하나의 클래스만 상속 가능 (다중 상속 불가능)
2. 기본적인 상위클래스의 (저장속성의) 메모리구조를 따라갈 수 밖에 없음
(필요하지 않은 속성/메서드도 상속됨)
3. 클래스(레퍼런스 타입)에서만 가능
- 영어로 규약이나 협약 -> 자격증 / 리모콘 같은 개념
class Student: Person, AProtocol, BProtocol { // A프로토콜, B프토토콜이 원하는 내용 구현해야 함 }
// (1) 정의 protocol MyProtocol { // "요구사항만"을 정의 (구체적인 구현 없음, 프로토콜을 채택한 곳에서 하게 됨) func doing() -> Int } } // (2) 채택 class Class: MyProtocol { } // (3)구현 func doing() -> Int { return 7 } // <상속이 있는 경우의 문법> class Person: Student, MyProtocol { ... } 1) 상속하려는 클래스 먼저 선언 2) 그 다음 채택하려는 프로토콜 선언
1️⃣ 속성의 요구사항을 정의
protocol MyProtocol { var id: String { get } var name: String { get set } static var type: String { get set } // 속성 의미의 var, let은 안됨 (get 받아와야하니까) }
✨ 인스턴스 속성 요구사항 - 최소한의 요구사항을 지정 (헤드부분-인풋,아웃풋의 형태만) - 저장속성 / 계산속성으로 모두 구현 가능 (프로토콜 요구사항만으로는 저장/계산 속성의 구별 불가)
✨ 타입(static) 속성 요구사항 - 최소한의 요구사항을 지정 - 저장 타입 속성 / 계산 타입 속성으로 모두 구현 가능 (저장 속성 재정의 불가 원칙) - 클래스에서 채택 시 계산 타입 속성에서 static / class 키워드로 모두 구현 가능 (타입 속성의 의미일 뿐 / class 키워드는 재정의 가능)
2️⃣ 메서드의 요구사항을 정의
protocol MyProtocol { func random() -> Int mutating func toggle() static func reset() }
✨ 메서드 요구사항 - 메서드의 헤드부분 (인풋/아웃풋)을 정의 - mutatigng 키워드 : (구조체로 제한하는 것은 아님) 구조체에서 저장 속성 변경하는 경우, 구조체도 채택 가능하도록 허락하는 키워드 - 타입 메서드 : 클래스에서 채택 시 static / class 키워드로 모두 구현 가능 (타입 메서드의 의미, class 키워드는 재정의 가능)
protocol MyProtocol { init() subscript(idx: Int) -> Int { get set } // 서브스크립트에서 get필수 (set선택) }
✨ 생성자 요구사항 - 생성자를 요구사항으로 지정 가능 1) 클래스에서 생성자 채택 시, (하위클래스를 고려) required(필수적)를 붙여야 함 2) 클래스가 final로 선언되려면 required 생략 가능 3) 클래스에서는 반드시 지정생성자로 구현할 필요 없음 (편의생성자로도 구현 가능) init?() 실패가능 생성자 선언 ➡️ init() / init!() 로 구현 (O) init() 실패불가능 생성자 선언 ➡️ init?() 로 구현 (X)
✨ 서브스크립트 요구사항 - 최소한의 요구 사항을 지정 { get } ➡ 읽기(get) / 읽기쓰기(get/set) { get set } ➡ 읽기쓰기(get/set)
✏️ 상속과 프로토콜 채택의 문법
extension SomeType: { // 프로토콜이 원하는 내용 구현 }
- 관습적으로 프로토콜의 채택은 확장(extension)에서 구현하는 것을 권장
15-4
✏️ 프로토콜은 타입이다.
- 스위프트는 프로토콜을 "일급객체" 로 취급
1) "프로토콜"을 변수에 할당할 수 있음
2) 함수를 호출할 때, "프로토콜"을 파라미터로 전달할 수 있음
3) 함수에서 "프로토콜"을 반환할 수 있음
// 프로토콜의 타입 취급의 장점 - 1 ⭐️
let electronic: [Remote] = [tv, sbox] // 프로토콜의 형식으로 담겨있음
for item in electronic { // 켜기, 끄기 기능만 사용하니
타입캐스팅을 쓸 필요도 없음
(다만, 프로토콜에 있는 멤버만 사용가능)
item.turnOn()
}
// 프로토콜의 타입 취급의 장점 - 2 ⭐️
func turnOnSomeElectronics(item: Remote) {
item.turnOn()
}
turnOnSomeElectronics(item: tv)
turnOnSomeElectronics(item: sbox)
✏️ is연산자 / as연산자 for 프로토콜
// 1) is연산자 ==============================
// 특정타입이 프로토콜을 채택하고 있는지 확인
tv is Remote
sbox is Remote
// 프로토콜 타입으로 저장된 인스턴스가 더 구체적인 타입인지 확인 가능
electronic[0] is TV
electronic[1] is SetTopBox
// 2) as연산자 ==============================
// 업캐스팅(as)
let newBox = sbox as Remote
newBox.turnOn()
newBox.turnOff()
// 다운캐스팅(as?/as!)
let sbox2: SetTopBox? = electronic[1] as? SetTopBox
sbox2?.doNetflix()
//(electronic[1] as? SetTopBox)?.doNetflix()
✏️ 프로토콜의 상속, 다중상속
✏️ 클래스 전용 프로토콜
protocol SomeType: AnyObject { // 최소한의 요구사항 }
- AnyObject는 프로토콜이다. 그렇기 때문에 범용적 타입으로 사용할 수 있었던 것이고,
다운 캐스팅(as?/as!)해서 구체적인 실제타입으로 사용할 수 있었던 것
✏️ 프로토콜의 합성
let some: AProtocol & BProtocol = 인스턴스
- 프로토콜을 &로 연결해서,
프로토콜 두 개를 병합해서 타입으로 사용하는 것 가능
ex) Aprotocol & BProtocol
(구현을 해도 되고 안해도 되는)
✏️ @어트리뷰트 키워드 짚고 가기
- 추가적인 정보를 제공하는 키워드
// (1) 선언에 추가정보 제공해주는 종류 @available(iOS 10.0) class MyClass { ... } // (2) 타입에 추가 정보 제공해주는 종류 func doSomething(completion: @escaping() -> ()) { ... } // - 컴파일러에게 추가적인 정보를 알려주는 역할 (2가지 종류)
✏️ 선택적 요구사항의 구현
요구사항을 강제가 아닌 선택적으로 만들기
@objec protocol MyProtocol { var name: String { get } @objc optional var isOn: Bool { get set } ... @objc optional func doSomething() }
✨ 선택적(optional) 멤버로 선언 - 프로토콜에서 요구사항 구현 시, 선택적인 멤버로 구현가능하도록 변형 가능 (다만, 본 기능은 오브젝티브C에서 지원하는 기능) - @objc키워드를 프로토콜의 선언앞에 붙여서, 추가적인 정보 제공 (오브젝티브C에서 읽을 수 있는 코드라는 의미) - @objc optional 을 멤버 앞에 선언 ➡ 해당 멤버는 선택적 요구사항으로 바뀜 (클래스 전용 프로토콜이기 때문에, 구조체 / 열거형에서는 채택하지 못함)
15-7
/**========================================
- (귀찮은 방식으로) 프로토콜을 채택한 모든 타입에서,
실제 구현을 계속적으로 반복해야하는 불편함을 덜기 위해
- "프로토콜 확장"을 제공해서 메서드의
디폴트 구현을 제공함 (코드의 중복을 피할 수 있음)
==========================================**/
extension Remote {
// (요구사항의 메서드 우선순위 적용 - 프로토콜 메서드 테이블 만듦)
func turnOn() { print("리모콘 켜기") }
// 1. (채택)구현시 해당 메서드 2. 기본 메서드
func turnOff() { print("리모콘 끄기") }
// 1. (채택)구현시 해당 메서드 2. 기본 메서드
func doAnotherAction() {
// (요구사항 메서드 X - 테이블 만들지 않음)
print("리모콘 또 다른 동작")
// 타입에 따른 선택 (Direct Dispatch)
}
}
✨ 클래스와 상속의 단점
1. 하나의 클래스만 상속가능 (다중상속 불가능)
2. 기본적인 상위클래스의 메모리구조를 따라갈 수 밖에 없음
(필요하지 않은 속성/ 메서드도 상속됨)
3. 클래스(레퍼런스타입) 에서만 가능
✨ 프로토콜 지향 프로그래밍
1. 여러개의 프로토콜 채택 가능 (다중상속과 유사)
2. 메모리 구조에 대한 특정 요구사항 없음
(필요한 속성/메서드만 채택도 가능 - @optional)
3. 모든 타입에서 채택 가능 (밸류타입도 가능)
protocol Bluetooth {
func blueOn()
func blueOff()
}
⬇️
extension Bluetooth where Self: Remote {
func blueOne() {print("블루투스 켜기")}
func blueOff() {print("블루투스 끄기")}
}