프로토콜
프로토콜이란?
프로토콜은 일종의 약속이라고 생각할 수 있다.
프로토콜을 어떻게 활용하냐면,
특정 컨트롤러에서 발생하는 각종 이벤트를 효율적으로 관리하기 위해 대리자(Delegate)를 지정하여 이벤트 처리를 위임하고 실제로 이벤트가 발생하면 위임된 대리자가 콜백 메소드를 호출해주는 델리게이트 패턴을 구현하기 위해 사용된다.
구현해야 하는 메서드의 명세가 작성되어 있고 단순한 선언 형태(구현 없이)로 구성되어 있다. 그러므로 구체적인 내용은 별도로 구현을 해야 한다. 이렇게 특정 프로토콜의 명세를 따르는 타입은 프로토콜을 준수한다고 표현할 수 있다.
구조체, 클래스, 열거형, 익스텐션에서 사용할 수 있고 실질적인 내용은 여기서 작성할 수 있다.
프로토콜과 부모 클래스의 차이점
만약 뷰 컨트롤러에서 계속해서 작성해야하는 부분이 있다면, 이를 protocol로 관리할 수도 있고 base view controller처럼 class로 관리할 수도 있다. 이 둘의 차이점이 무엇일까?
- 프로토콜은 특정 클래스 전체를 책임지지 않고 특정 뷰 객체 또는 기능에 대한 몇 가지만 담당한다.
- 클래스는 단일 상속만 허용되지만 프로토콜은 개수 제한이 없다.
- 프로토콜 선언 순서는 상관 없지만 준수해야 하는 필수 프로퍼티, 필수 메서드 등은 모두 구현해야 한다.
- 그렇기 때문에 프로토콜을 채택한 경우에는 자신을 선택한 타입에게 어떤 프로퍼티와 메서드를 구현해야 하는지 요구할 수 있다.
프로토콜 메서드
프로토콜에서 메서드를 구현할 경우, 중괄호 블럭을 사용하지 않는다. (구현부를 작성하지 않는다.)
메서드의 이름, 파라미터의 이름 및 반환 타입을 정의할 수 있지만 실제 실행 내용은 작성할 수 없다. 메서드의 파라미터 기본값 역시 불가능하다.
프로토콜에서는 인스턴스 메서드와 타입 메서드를 정의할 수 있다.
값 타입(구조체와 같은)에서 사용할 경우, mutating 키워드를 사용해 인스턴스에서 변경 가능하다는 것을 표시할 수 있다.
프로토콜 프로퍼티
프로퍼티가 저장 프로퍼티인지, 연산 프로퍼티인지 명시하지 않는다.
따라서 구현부에서 프로퍼티를 저장 프로퍼티로 사용할 수도 있고 연산 프로퍼티로 사용할 수도 있다.
-> 단지 이름과 타입, gettable / settable 한지만 명시한다.
(만약, 읽고 쓰는 프로퍼티를 요구한다면 read-only 프로퍼티를 구현할 수 없다.)
프로퍼티는 항상 var로 선언해야 한다. (어떻게 사용될 지 모르기 때문에)
@objc protocol ViewPresentableProtocol {
var navigationTitleString: String { get set }
var backgroundColor: UIColor { get }
static var identifier: String { get }
func configureView()
@objc optional func configureLabel()
@objc optional func configureTextField()
}
만약 프로토콜에서 gettable만 명시했을 경우 프로토콜 구현 시 get을 사용해야 하며 set을 추가로 구현하는 것은 선택사항이다.
(= get 기능만 명시했으므로 get 기능만 잘 구현되어 있다면 요구사항을 저장 프로퍼티로 구현하든 연산 프로퍼티로 구현하든 상관이 없다. 즉, 필요하다면 settable도 구현할 수 있다.)
같은 맥락으로 gettable, settable 모두 명시했다면 프로토콜 구현 시, get과 set을 모두 구현해야 한다.
연산 프로퍼티, 저장 프로퍼티 둘 다 구현하여 사용할 수 있다.
연산 프로퍼티를 사용한 경우는 아래와 같다.
class ViewController: UIViewController, NavigationUIProtocol {
// 연산 프로퍼티 사용
var titleString: String {
get {
return "소연이의 일기장"
}
set {
title = newValue
}
}
var mainTintColor: UIColor {
return .systemPink
}
override func viewDidLoad() {
titleString = "새로운 일기장"
view.backgroundColor = backgroundColor
}
}
저장 프로퍼티를 사용한 경우는 아래와 같다.
class ViewController: UIViewController, ViewPresentableProtocol {
// 저장 프로퍼티 사용
static let identifier: String = "ViewController"
var navigationTitleString: String = "소연님의 다마고치"
let backgroundColor: UIColor = .black
func configureView() {
title = navigationTitleString
view.backgroundColor = backgroundColor
}
}
옵셔널 프로퍼티
프로토콜 구현 시 모든 내용을 구현해야 하고 구현하지 않았을 때는 필요한 항목의 구현이 누락되었다는 오류 메시지가 나타난다.
그러나, 구현하는 객체에 따라서 필요하지 않는 요소가 존재할 수 있기 때문에 이를 방지하기 위해서는 옵셔널 형태로 지정할 수 있다.
단 클래스에서만 선택적 요청 구현이 가능하다.
@objc를 통해 Objective-C에서도 참조할 수 있도록 구현해야 하는데 Objective-C에서는 클래스만 존재하기 때문이다.
@objc protocol OrderSystem: AnyObject {
func recommandEventMenu()
@objc optional func askStampCard(count: Int) -> String
}
class Smoothie: OrderSystem {
func recommandEventMenu() {
print("스무디 이벤트 행사를 진행합니다.")
}
// optional인 askStampCard는 구현하지 않아도 된다.
}
iOS Framework에서의 프로토콜
CaseIterable 프로토콜
- 열거형의 값을 배열 컬렉션과 같이 순회할 수 있도록 도와주는 프로토콜이다.
- 배열의 메서드를 사용할 수 있으며, for in 등으로도 순회할 수 있다.
- 열거형 타입 프로퍼티인 allCases를 활용하여 배열처럼 사용할 수 있다.
enum SettingSection: Int, CaseIterable {
case authorization
case onlineShop
case question
var description: String {
switch self {
case .authorization:
return "알림 설정"
case .onlineShop:
return "온라인 스토어"
case .question:
return "Q & A"
}
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return SettingSection.allCases.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return SettingSection.allCases[section].description
}
Hashable 프로토콜
- 정수 Hash 값을 제공하는 타입의 프로토콜이다.
- Hashable을 준수하면 Dictionary의 키가 될 수 있는 타입이 된다. (= 딕셔너리의 키 값은 Hashable 프로토콜을 만족하는 타입이어야 한다.)
- 다르게 말하면, value가 hashable 해야 할 때, Hashable 프로토콜을 채택해서 구현해주면 hashable이 보장된 정수 해시 값을 제공한다.
'Swift' 카테고리의 다른 글
First-Class Citizen(일급 객체) (0) | 2022.08.09 |
---|---|
Singleton Pattern (0) | 2022.08.05 |
프로퍼티 - 저장 프로퍼티 (0) | 2022.07.27 |
some (0) | 2022.04.29 |
Any/AnyObject (0) | 2022.04.29 |