[Swift] 프로토콜 (protocol)

Kiwi·2024년 3월 18일

Swift

목록 보기
16/21
post-thumbnail

📁 프로토콜

Swift의 프로토콜은 메서트, 프로퍼티, 이니셜라이저 및 다른 특정 작업 또는 기능에 대한 청사진이다. 즉 영어 프로토콜의 의미처럼, 특정 역할을 하기 위한 메소드, 프로퍼티, 기타 요구사항 등을 정의 해놓은 “규약” 혹은 “약속”이라고 생각하면 된다. 클래스, 구조체, 열거형이 프로토콜을 채택하고 모든 요구사항을 충족하면 프로토콜을 준수했다고 한다.(준수하지 않을수도 있는건가..?🤨) 즉, 프로토콜을 채택하면 그 프로토콜의 내용을 따라야한다는 약속이 생겨버리는 것이다!🫨 프로토콜에서 정의한 코드는 꼭 구현해야한다는 말이다.

프로토콜의 정의

앞에서 프로토콜이 class, structure, enum의 특정 역할을 하기 위한 메소드, 프로퍼티, 기타 요구사항 등을 정의해놓은 규약이라고 했다. class, structure, enum은 프로토콜을 채택해서 특정 기능을 실행하기 위한 프로토콜의 요구사항을 실제로 구현할 수 있다. 즉, 프로토콜은 설계된 조건만 정의를 하고 제시를 할 뿐 스스로 기능을 구현하지 않는다. 프로토콜을 정의하는 방법은 다음과 같다.

protocol 프로토콜이름 {
 // 프로토콜 정의
}

// 상속받는 클래스의 프로토콜 채택
class 클래스이름: 슈퍼클래스, 프로토콜1, 프로토콜2 {
 // 클래스 정의
}

실제로 사용하는 예시 코드는 다음과 같다.

protocol Vehicle {
    var speed: Double {get set}     
    var manufacturer: String {get}	//읽기전용속성
}

class Car: Vehicle {
    var speed: Double = 0.0
    var manufacturer: String = "Tesla"
}

class Bicycle: Vehicle {
    var speed: Double = 0.0
    var manufacturer: String {return "samJung"}
}

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 가능

예시코드를 보면 프로토콜로 vehicle은 무조건 speed 프로퍼티와 manufacturer 메서드를 정의해야 한다고 규약을 정한것이다. 그리고 그 프로토콜을 채택하는 Car와 Bicycle 클래스는 프로퍼티와 manufacturer를 정의하고 있다. 이때 주의해야할 점이 있다.

✏️ 프로퍼티

프로토콜 내의 프로퍼티는 무조건 var로 정의해야한다! 그리고 예시에서 get set구문을 보았듣이 연산프로퍼티로 정의해도 되고 저장프로퍼티로 정의해도 된다. 또한 프로퍼티의 타입까지 명시해주어야지 일관성 있게 사용될 수 있다. 물론 무조건은 아니지만 let을 사용하면 읽기전용으로만 구현할 수 있다.

✏️ 메서드

프로토콜에서 메서드를 정의할 때는 메서드의 구체적인 구현코드를 적지 않는다. 그래서 {}를 생략한다. 다만 메서드의 파라미터, 반환값이 있다면 반드시 명시해주어야 한다. 만약 struct에서 mutating 키워드를 사용한다면 이 사실도 프로토콜에서 먼저 정의해주어야 한다.


프로토콜을 공부해보니 상속과 매우 유사해보인다. 둘다 코드를 모듈화하는 것은 비슷하지만 다른점이 있다. 먼저, 프로토콜은 class이외에 struct나 enum도 채택할 수 있다. 그리고 상속은 다중 상속이 불가능하지만 프로토콜은 다중채택이 가능하다!

associatedtype

associatedtype은 프로토콜 내에서 실제 타입을 명시하지 않고, 해당 프로토콜을 채택하는 타입에서 실제 타입을 결정하도록 하는데 사용된다. 프로토콜에서 특정 메서드, 속성 또는 서브스크립트의 반환 타입이나 매개변수 탕비으로 구체적인 타입을 명시하지 않고 대신 associatedtype으로 선언하여 프로토콜을 채택하는 타입에서 실제 타입을 정의할 수 있다.

typealias

typealias는 기존 타입에 대해 새로운 이름을 지정하거나 복잡한 타입에 대한 간결한 별칭을 생성할 때 사용된다. 코드를 읽기 쉽게 만들거나 여러 번 사용되는 긴 타입 이름을 간략하게 대체할때 유용하다.

protocol Container {
	associated Item
    var count: Int { get }
    mutating func append(_item: Item)
    func item(at index: Int) -> Item
}

struct IntContainer: Container {
	typealias Item = Int
    var items = [Item]()
    
    var count: Int {
    	return items.count
    }
}

✏️ @objc

특정 프로토콜을 채택하면 프로토콜에 있는 메서드를 전부 다 상세히 구현해야하는 것은 아니다. 프로토콜에서 필수로 구현해야 하는 메서드를 선언할 때는 그 메서드를 protocol 정의 내부에 직접 선언한다. 반대로, 선택적으로 구현해도 되는 메서드는 @objc 키워드와 optional 키워드를 함께 사용하여 선언한다.

protocol MyProtocol {
    // 필수로 구현해야 하는 메서드
    func func1()

    // 선택적으로 구현해도 되는 메서드
    @objc optional func func2()
}

이런 원리로 UITableViewDataSource를 채택할때 필수로 구현하는 메서드(행의 갯수 등)가 있는 반면 선택적으로 구현해도 되는 메서드가 있는 것이다.

profile
🐣 iOS Developer

0개의 댓글