프로토콜(Protocol)은 특정 역할을 수행하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진을 정의한다.
구조체, 클래스, 열거형은 프로토콜을 채택(Adopted)함으로써 특정 기능을 실행하기 위한 프로토콜의 요구사항을 실제로 구현할 수 있다.
어떤 프로토콜의 요구사항을 모두 따르는 타입은 '프로토콜을 준수한다(Conform)'고 표현한다. 프로토콜의 요구사항을 충족시키기 위해서는 프로토콜이 제시하는 기능을 모두 구현해주어야 한다. 즉, 프로토콜은 정의와 제시만 할 뿐이지 스스로 기능을 구현하지는 않는다.
특정 타입의 인스턴스를 만들어서 사용하려는 사람 입장에서는 프로토콜을 통해 해당 타입이 수행할 수 있는 기능들에 대해 명확하게 알 수 있고, 컴파일러 또한 이를 쉽게 파악할 수 있도록 해준다.
프로토콜은 protocol 키워드를 사용해서 정의해 줄 수 있다.
protocol 프로토콜 이름 {
프로토콜 정의
}
/* 다중 상속도 가능 */
protocol 프로토콜 이름: 부모 프로토콜 이름 목록 {
프로토콜 정의
}
- 프로토콜 정의 예시
Talkable 이라는 프로토콜을 정의해주었다.
이 프로토콜을 준수하려면 요구사항대로 topic, language, talk(), 이니셜라이저를 구현해주어야 한다.
- 프로토콜 채택 및 준수
Talkable 프로토콜을 채택하는 Person 구조체를 정의하면서 모든 요구사항을 준수하도록 해준다.
- 프로토콜 상속
프로토콜은 여러개 다중 상속이 가능하며, 여러 프로토콜을 상속받은 프로토콜을 채택하려면 해당하는 모든 프로토콜의 요구사항을 준수해야 한다.
- 클래스 상속 + 프로토콜 채택
클래스 정의 시 클래스 상속과 프로토콜 채택을 동시에 하려면
상속받으려는 클래스를 먼저 명시하고, 그 뒤에 채택할 프로토콜들을 작성해준다.
- 프로토콜 준수 확인
is, as 연산자를 사용하여 인스턴스가 특정 프로토콜을 준수하는지 확인할 수 있다.
- 실습 코드
/* 프로토콜 정의 */
protocol Talkable {
/* 프로퍼티 요구 */
// 프로퍼티 요구는 항상 var 키워드를 사용
// get : 읽기만 해도 상관 없다는 뜻
// get, set을 모두 명시하면 읽기 쓰기가 모두 가능한 프로퍼티여야 한다.
var topic: String { get set }
var language: String { get }
// 메서드 요구
func talk() // 프로토콜에서는 요구만 하고, 실제 구현은 프로토콜을 채택하는 타입에서 해준다.
// 이니셜라이저 요구
init(topic: String, language: String)
}
/* 프로토콜 채택 및 준수 */
// Person 구조체는 Talkable 프로토콜을 채택함
struct Person: Talkable {
var topic: String // 읽기 쓰기 모두 가능한 프로퍼티이기 때문에 변수로 선언해줘야함
let language: String // 읽기 전용 프로퍼티이기 때문에 상수로 선언 (var로 해도 되긴 됨)
// // 읽기 전용 프로퍼티 요구는 연산 프로퍼티로 대체 가능
// var language: String { return "한국어" }
//
// // 읽기, 쓰기 프로퍼티도 연산 프로퍼티로 대체 가능
// var subject: String = ""
// var topic: String {
// set {
// self.subject = newValue
// }
// get {
// return self.subject
// }
// }
func talk() {
print("\(topic)에 대해 \(language)로 말합니다.")
}
init(topic: String, language: String) {
self.topic = topic
self.language = language
}
}
/* 프로토콜 상속 */
// 프로토콜은 클래스와 다르게 다중상속이 가능하다.
protocol Readable {
func read()
}
protocol Writeable {
func write()
}
protocol ReadSpeakable: Readable {
// func read() // Readable을 상속 받았기 때문에 read()도 요구한다고 볼 수 있음
func speak()
}
protocol ReadWriteSpeakable: Readable, Writeable {
// func read()
// func write()
func speak()
}
struct SomeType: ReadWriteSpeakable {
// read(), write(), speak()를 모두 구현해주어야 프로토콜을 준수하게 됨
// 하나라도 구현하지 않으면 오류
func read() {
print("Read")
}
func write() {
print("Write")
}
func speak() {
print("Speak")
}
}
/* 클래스 상속과 프로토콜 */
class SuperClass: Readable {
func read() { print("read") }
}
class SubClass: SuperClass, Writeable, ReadSpeakable {
func write() { print("write") }
func speak() { print("speak") }
}
/* 프로토콜 준수 확인 - is, as 연산자 사용 */
let sup: SuperClass = SuperClass()
let sub: SubClass = SubClass()
var someAny: Any = sup
someAny is Readable // true
someAny is ReadSpeakable // false
someAny = sub
someAny is Readable // true
someAny is ReadSpeakable // true
someAny = sup
if let someReadable: Readable = someAny as? Readable {
someReadable.read()
} // read
if let someReadSpeakable: ReadSpeakable = someAny as? ReadSpeakable {
someReadSpeakable.speak()
} // 동작하지 않음
someAny = sub
if let someReadable: Readable = someAny as? Readable {
someReadable.read()
} // read
if let someReadSpeakable: ReadSpeakable = someAny as? ReadSpeakable {
someReadSpeakable.speak()
} // speak
'Swift' 카테고리의 다른 글
[ Swift ] Swift 기초 문법15 - 익스텐션 (0) | 2023.02.19 |
---|---|
[ Swift ] Swift 기초 문법13 - assert와 guard (0) | 2023.02.19 |
[ Swift ] Swift 기초 문법12 - 타입캐스팅 (0) | 2023.02.19 |
[ Swift ] Swift 기초 문법11 - 옵셔널 체이닝과 nil 병합 연산자 (0) | 2023.02.19 |
[ Swift ] Swift 기초 문법10 - 인스턴스의 생성과 소멸 (0) | 2023.02.19 |