프로토콜
메서드, 속성 및 기타 요구사항의 청사진을 정의하여 클래스,
구조체 또는 열거형에서 구현할 수 있는 일종의 인터페이스인 '프로토콜'에 대해 알아봅시다.
- 특정 역할을 하기 위한 메소드, 프로퍼티, 기타 요구사항 등을 정의 해놓은 “규약” 혹은 “약속”
- "class", "structure", "enum"이 프로토콜을 ‘채택’하고 모든 요구사항을 충족하면 프로토콜을 ‘준수’함
- class, structure, enum이 프로토콜을 채택해서 특정 기능을 실행하기 위한 요구사항을 실제로 구현
- 설계된 조건만 정의를 하고 제시를 할 뿐 스스로의 기능 구현은 안합니다.
- 프로토콜에서는 이름과 타입 그리고 gettable, settable을 명시
- 프로퍼티는 항상 var로 선언
- 메서드를 정의할 때 메서드 이름과 리턴값을 지정할 수 있고, {}(구현 코드)는 적지 않습니다.
- 상속과 유사하다고 볼 수도 있겠지만 class 이외에 struct나 enum도 프로토콜을 채택하는 특징을 지님
- 상속은 다중 상속이 불가능, 프로토콜은 다중 상속 가능 (확장성이 높음)
// 프로토콜 예시 1
protocol 프로토콜이름 {
// 프로토콜 정의
}
// 상속받는 클래스의 프로토콜 채택
class 클래스이름: 슈퍼클래스, 프로토콜1, 프로토콜2 { // 상속의 경우 콤마(,)를 통해서 상속과 프로토콜 채택을 동시에 사용
// 클래스 정의
}
protocol Vehicle {
var speed: Double { get set } // get과 set을 모두 요구하는 가변 속성
var manufacturer: String { get } // 읽기 전용 속성
/*
speed가 현재 얼마인지를 'get' 얻어 올 수 있으며, speed가 몇인지 'set' 넣어줄 수 있다는 의미입니다.
manufacturer는 'get' 값을 얻어 올 수 있음을 이야기 합니다.
*/
}
class Car: Vehicle { // Vehicle이라는 프로토콜을 Car가 채택을 한 코드
var speed: Double = 0.0 // get과 set이 요구되는 속성을 구현
var manufacturer: String = "Toyota" // 읽기 전용 속성을 구현
}
class Bicycle: Vehicle { // class이기 때문에 실제로 값을 넣어주어야 하며, 넣고 싶지 않다면 init을 만들어 주어야 합니다.
var speed: Double = 0.0 // get과 set이 요구되는 속성을 구현
var manufacturer: String { return "Giant" } // 읽기 전용 속성을 연산 프로퍼티로 구현
/*
speed는 get과 set이 요구
manufacturer get 읽기가 가능한 형태이다.
*/
}
/*
Car() 인스턴스를 만들고 여기에 speed는 60.0 이는 set이며
값을 넣어주는 것이며, car의 speed를 get 할수도 있습니다.
그리고 car의 manufacturer를 get할수도 있습니다.
*/
let car = Car()
car.speed = 60.0 // set 가능
print(car.speed) // get 가능
print(car.manufacturer) // get 가능
let bike = Bicycle()
bike.speed = 20.0 // set 가능
print(bike.speed) // get 가능
print(bike.manufacturer) // get 가능
// 프로토콜 예시 2
protocol Student {
var studentId: Int { get set }
var name: String { get }
func printInfo() -> String
}
struct UnderGraduateStudent: Student {
var studentId: Int
var name: String
var major: String
func printInfo() -> String {
return "\(name), whose student id is \(studentId), is major in \(major)"
}
}
struct GraduateStudent: Student {
var studentId: Int
var name: String
var degree: String
var labNumber: Int
func printInfo() -> String {
return "\(name), member of lab no.\(labNumber), has a \(degree) degree"
}
}
// 프로토콜은 타입으로서도 사용가능
let underGraduate: Student = UnderGraduateStudent(studentId: 1, name: "홍길동", major: "computer")
let graduate: Student = GraduateStudent(studentId: 2, name: "김철수", degree: "master", labNumber: 104)
let studentArray: [Student] = [underGraduate, graduate]
// 프로토콜의 다중상속
protocol Coordination
{
var top: String { get set }
var pants: String { get set }
init(top: String, pants: String)
func checkCoordination()
}
protocol Hair {
var hair: String { get }
func checkHairStyle()
}
struct Person: Coordination, Hair { // 코디와 헤어스타일을 두개다 챙기기위해서..
var top: String
var pants: String
let hair: String = "포마드"
func checkHairStyle() {
print("오늘의 헤어스타일은 \(hair)스타일")
}
func checkCoordination() {
print("상의: \(top)\n하의: \(pants)")
}
init(top: String, pants: String) { // 위 init에서 선정한 대로
self.top = top
self.pants = pants
}
}
let safari: Person = Person(top: "긴팔", pants: "반바지")
safari.checkHairStyle()
safari.checkCoordination()
//오늘의 헤어스타일은 포마드스타일
//상의: 긴팔
//하의: 반바지
associatedtype, typealias
associatedtype
- 프로토콜 내에서 실제 타입을 명시 X
- 해당 프로토콜을 채택하는 타입에서 실제 타입을 결정에 사용
- 프로토콜에서 특정 메서드, 속성 또는 서브스크립트의 반환 타입이나
- 매개변수 타입으로 구체적인 타입을 명시하지 않고
대신 "associatedtype"으로 선언하여
프로토콜을 채택하는 타입에서 실제 타입을 정의
typealias
- 기존 타입에 대해 새로운 이름을 지정하거나 복잡한 타입에 대한 간결한 별칭을 생성할 때 사용
- 코드를 읽기 쉽게 만들거나 여러 번 사용되는 긴 타입 이름을 간략하게 대체할 때 유용
// associatedtype, typealias 예시
/*
Container 라는 프로토콜이 있습니다.
Item이라는 타입은 만든것도 아니며, 기본형도 아닙니다.
Item은 제네릭과 엇비슷한 느낌입니다. (제네릭의 경우 T를 사용해서 모든 타입이 들어올 수 있었습니다.)
"typealias Item = Int" Item 타입에 Int를 넣었는데, 이는 Item이라는 타입을 쓸 것이며 별칭을 뜻합니다.
즉, Item의 타입은 Int 타입으로 정의가 되었고, Item이 쓰인 전부가 Int 타입으로 치환되었습니다.
*/
protocol Container {
associatedtype Item // 연관 타입
var count: Int { get }
mutating func append(_ item: Item)
func item(at index: Int) -> Item
}
struct IntContainer: Container {
typealias Item = Int // 연관 타입을 Int로 typealias하여 구현
var items = [Item]() // Int의 배열
var count: Int {
return items.count
}
mutating func append(_ item: Item) {
items.append(item)
}
func item(at index: Int) -> Item { // item 이건 타입이 아니라 메서드 이름 입니다.
return items[index]
}
}
var intBox = IntContainer()
intBox.append(5)
intBox.append(10)
print(intBox.item(at: 0)) // 출력: 5
/*
위 예시에서 Container 프로토콜은 Item이라는 연관 타입을 가지고 있습니다.
이 연관 타입은 Container 프로토콜을 채택하는 구체적인 타입에서 실제 타입으로 정의
IntContainer 구조체에서 Item을 Int로 typealias하여 실제 타입을 정의,
이를 사용하여 배열에 Int 값을 저장하고 반환하는 메서드를 구현
*/
'IOS > Swift-Study' 카테고리의 다른 글
| [Swift-Study] 심화 문법종합반 2주차 5일차 정리 - 제네릭 (2) | 2024.03.27 |
|---|---|
| [Swift-Study] 심화 문법종합반 2주차 4일차 정리 - 확장 (0) | 2024.03.27 |
| [Swift-Study] 심화 문법종합반 2주차 4일차 정리 - ARC와 메모리 누수 (0) | 2024.03.26 |
| [Swift-Study] 심화 문법종합반 2주차 3일차 정리 - 예외처리 (0) | 2024.03.25 |
| [Swift-Study] 심화 문법종합반 2주차 3일차 정리 - 고차함수 (0) | 2024.03.25 |