프로토콜의 상속
- 프로토콜도 상속이 가능 / 다중 상속이 가능
- 여러가지 요구사항의 나열일뿐
import UIKit
protocol Remote {
func turnOn()
func turnOff()
}
protocol AirConRemote {
func Up()
func Down()
}
protocol SuperRemoteProtocol: Remote, AirConRemote {
// 프로토콜끼리, 상속 구조를 만드는 것이 가능
// 자동으로 상위 프로토콜 메서드가 정의
// func turnOn()
// func turnOff()
// func Up()
// func Down()
func doSomething()
}
// 프로토콜의 채택 및 구현
class HomePot: SuperRemoteProtocol {
func turnOn() { }
func turnOff() { }
func Up() { }
func Down() { }
func doSomething() { }
}
- 클래스 전용 프로토콜 (AnyObject)
// 클래스 전용 프로토콜로 선언
// (AnyObject프로토콜을 상속)
protocol SomeProtocol: AnyObject { // AnyObject는 클래스 전용 프로토콜
func doSomething()
}
// 구조체에서는 채택할 수 없게 됨 ⭐️
//struct AStruct: SomeProtocol {
//
//}
// 클래스에서만 채택 가능
class AClass: SomeProtocol {
func doSomething() {
print("Do something")
}
}
- 프로토콜 합성(Protocol Composition) 문법
- 프로토콜을 합성하여 임시타입으로 활용 가능
- 프로토콜 두개를 병합해서 사용하는 문법(&로 연결)
import UIKit
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
print("생일 축하!, \(celebrator.name), 이제 \(celebrator.age)살이야!")
}
let birthdayPerson = Person(name: "포든", age: 25)
wishHappyBirthday(to: birthdayPerson)
// 임시적인 타입으로 저장 (두개의 프로토콜을 모두 채택한 타입만 저장 가능)
let whoIsThis: Named & Aged = birthdayPerson
프로토콜의 선택적 요구사항의 구현
- 채택한 곳에서 구현을 해도 되고, 안해도 되는 요구사항
- Optional Protocol Requirements
- [어트리뷰트(attribute)]
- 컴파일러에게 알려주는 특별한 신호이자, 추가적인 정보를 제공(2가지 종류가 존재)
- 선언에 대한 추가정보 제공
- 타입에 대한 추가정보 제공
// 1) 선언에 대한 추가정보 제공
@available(iOS 10.0)
class MyClass {
...
}
// 2) 타입에 추가정보 제공
func doSomething(completion: @escaping () -> ()) {
...
}
- 선택적인(구현해도 되고 안해도 되는) 멤버 선언하기
- 프로토콜에서 요구사항 구현시, 선택적인 멤버로 구현가능 하도록 변형가능
- 다만, 본 기능은 오브젝티브C에서 지원하는 기능임
- @objc키워드를 프로토콜의 선언앞에 붙여서 추가적인 정보 제공
- 오브젝티브C에서 읽을 수 있는 코드라는 의미
- @ objc optional 을 멤버 앞에 선언 → 해당 멤버는 선택적 요구사항으로 바뀜
- 클래스 전용 프로토콜이기 때문에 구조체 / 열거형에서는 채택하지 못함
- 프로토콜에서 요구사항 구현시, 선택적인 멤버로 구현가능 하도록 변형가능
@objc protocol MyProtocol {
var name: String { get }
@objc optional var is isOn: Bool { get set }
...
@objc optional func doSomething()
}
프로토콜의 확장(Protocl Extension)
- 프로토콜의 확장 - 프로토콜 지향 프로그래밍 🚌
- 프로토콜을 채택한 모든 타입에서, 실제 구현을 계속적으로 반복해야하는 불편함을 덜기 위해 “확장” 을 제공해서 메서드의 디폴트 구현을 제공
- 코드의 중복을 피할 수 있음
import UIKit
protocol Remote {
func turnOn()
func turnOff()
}
extension Remote {
func turnOn() { print("리모콘 온") }
func turnOff() { print("리모콘 오프") }
func doAnotherAction() {
print("리모콘 다른 동작")
}
}
- 요구사항의 메서드 우선순위 적용 - 프로토콜 메서드 테이블을 제작
- turnOn, turnOff
- (채택) 구현시 해당 메서드
- 기본 메서드
- doAnotherAction
- 요구사항 메서드가 아님 → 테이블을 만들지 않음
- 타입에 따른 선택 (Direct Dispatch)
- turnOn, turnOff
- 프로토콜의 확장을 통한 다형성 제공 - 프로토콜 지향 프로그래밍
- 클래스, Data 영역에 (프로토콜을 위한) 테이블을 추가로 만듦0
import UIKit
protocol Remote {
func turnOn()
func turnOff()
}
extension Remote {
func turnOn() { print("리모콘 온") }
func turnOff() { print("리모콘 오프") }
func doAnotherAction() {
print("리모콘 다른 동작")
}
}
// 클래스 ⭐️
class Ipad: Remote {
func turnOn() { print("ipad on") }
func doAnotherAction() { print("ipad another action") }
}
/** [Class Virtual Table]
- func turnOn() : ipad on
- func turnOff() : 리모콘 오프
- func doAnotherAction : ipad another action
**/
let ipad: Ipad = Ipad()
ipad.turnOn() // 클래스 - VTable
ipad.turnOff() // 클래스 - VTable
ipad.doAnotherAction() // 클래스 - VTable
/**
[Protocol Witness Table] - 요구사항
- func turnOn() : ipad on
- func turnOff() : 리모콘 오프
**/
let ipad2: Remote = Ipad()
ipad2.turnOn() // 프로토콜 - WTable(Witness Table)
ipad2.turnOff() // 프로토콜 - WTable
ipad2.doAnotherAction() // 프로토콜 - Direct (직접 메서드 주소 삽입)
// 구조체 ⭐️
struct SmartPhone: Remote {
func turnOn() {
print("스마트폰 켜기")
}
func doAnotherAction() {
print("스마트폰 다른 동작")
}
}
/**
[구조체] - 메서드 테이블이 없음
**/
// 본래의 타입으로 인식했을때
var iphone: SmartPhone = SmartPhone()
iphone.turnOn() // 구조체 - Direct (직접 메서드 주소 삽입)
iphone.turnOff() // 구조체 - Direct (직접 메서드 주소 삽입)
iphone.doAnotherAction() // 구조체 - Direct (직접 메서드 주소 삽입)
// 스마트폰 켜기
// 리모콘 오프
// 스마트폰 다른 동작
/**=====================================
[Protocol Witness 테이블] - 요구사항
- func turnOn() { print("스마트폰 켜기") }
- func turnOff() { print("리모콘 오프") }
========================================**/
// 프로토콜의 타입으로 인식했을때
var iphone2: Remote = SmartPhone()
iphone2.turnOn() // 프로토콜 - W테이블
iphone2.turnOff() // 프로토콜 - W테이블
iphone2.doAnotherAction() // 프로토콜 - Direct (직접 메서드 주소 삽입)
// 스마트폰 켜기
// 리모콘 오프
// 리모콘 다른 동작
프로토콜지향 프로그래밍
- 상속의 관점에서 생각해보는 프로토콜
- 클래스와 상속의 단점
- 하나의 클래스만 상속가능 (다중 상속 불가능)
- 기본적인 상위클래스의 메모리 구조를 따라갈 수 밖에 없음
- 필요없는 속성, 메서드도 상속됨
- 클래스(레퍼런스 타입)에서만 가능
- 프로토콜 지향 프로그래밍
- 여러개의 프로토콜 채택 가능 (다중 상속과 유사)
- 메모리 구조에 대한 특정 요구사항 없음
- 필요한 속성/메서드만 채택도 가능 - @optional
- 모든 타입에서 채택 가능 (값 타입도 가능)
- 확장에서 구체적정의(재정의 가능)
- 채택하는 타입 제약도 가능
- 타입으로 사용가능하므로 활용성⬆️
- 조합의 장점을 잘 살려서, 보다 나은 구성,재사용성을 높일 수 있음
- 프로토콜 지향 프로그래밍을 잘 사용하면, 애플이 이미 만들어 놓은 데이터타입에도 채택하여 활용 가능
프로토콜 확장의 적용 제한
- 확장의 적용을 제한
- 프로토콜 확장에서, where 절을 통해, 프로토콜 확장의 적용을 제한 가능
- 즉, Remote 프로토콜을 채택한 타입에만 Bluetooth 프로토콜의 확장이 적용
- Remote 프로토콜을 채택하지 않은 타입은 Bluetooth 프로토콜을 채택할 수 있지만, 확장(기본구현)은 제공이 안됨 (무조건 직접 구현 해야함)
protocol Bluetooth {
func blueOn()
func blueOff()
}
extension Bluetooth where Self: Remote {
func blueOn() { print("bluetooth on") }
func blueOff() { print("bluetooth off") }
}
반응형
'iOS > Swift' 카테고리의 다른 글
Swift No.23 (1) | 2024.01.31 |
---|---|
Swift No.22 (0) | 2024.01.30 |
Swift No.20 (1) | 2024.01.27 |
Swift No.19 (1) | 2024.01.25 |
Swift No.18 (1) | 2024.01.23 |