- swift에 대해 공부한 내용을 정리한 글입니다.
- 해당 글은 한빛미디어의 스위프트 프로그래밍 3판을 참고하여 작성했습니다.
프로토콜은 특정 역할을 하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진을 정의한다. 구조체, 클래스, 열거형은 프로토콜을 채택해서 특정 기능을 실행하기 위한 프로토콜의 요구사항을 구현할 수 있다. 즉, 프로토콜은 정의와 제시만을 할 뿐이지 스스로 기능을 구현하지는 않는다.
잠깐 클래스의 상속을 떠올려보자. 우리는 클래스를 사용할 때 중복될만한 기능을 부모클래스에 정의해두고, 여러 자식클래스에서 상속받아 사용할 수 있다.
예를 들어, 클래스A에는 메서드A가, 클래스B에는 메서드B가 정의되어 있다. 또 다른 클래스C,D,E는 모두 메서드A를 필요로하고, 클래스C,D는 메서드B도 필요로한다.
클래스C,D,E는 메서드A를 사용하기 위해서 클래스A를 부모클래스로 삼아 상속받을 것이다.
하지만 클래스C,D는 메서드B도 필요하다. 이미 클래스A로부터 상속을 받았기 때문에 메서드B는 재사용성을 고려하지 않고 각각 구현해야 할까?
이때 사용할 수 있는 것이 바로 프로토콜이다. 프로토콜은 특정 역할을 하기위한 여러 기능들을 정의하며, 모든 클래스가 프로토콜을 채택할 수 있다. 이러한 점은 코드의 재사용성과 확장성을 지향한다.
이 밖에도 프로토콜은 다양하게 사용될 수 있으며, 개발에 꼭꼭! 필요하니 잘 알아두자.
프로토콜은 구조체나 클래스, 열거형의 모양과 비슷하게 정의할 수 있으며 protocol 키워드를 사용한다.
protocol 프로토콜 이름 {
프로토콜 정의
}
구조체, 클래스, 열거형 등에서 프로토콜을 채택하려면, 타입 이름 뒤에 콜론(:)을 붙여준 후 채택할 프로토콜 이름을 쉼표(,)로 구분하여 명시한다.
protocol SomeProtocol {
}
protocol SomeProtocol2 {
}
struct SomeStruct: SomeProtocol, SomeProtocol2 {
}
class SomeClass: SomeProtocol, SomeProtocol2 {
}
enum SomeEnum: SomeProtocol, SomeProtocol2 {
}
class ChildClass: ParentClass, SomeProtocol, SomeProtocol2 {
}
프로토콜은 타입이 특정 기능을 실행하기 위해 필요한 기능을 요구한다. 프로토콜이 자신을 채택한 타입에 요구하는 사항은 프로터티나 메서드와 같은 기능들이다.
프로토콜은 자신을 채택한 타입이 어떤 프로퍼티를 구현해야 하는지 요구할 수 있다. 이때 프로토콜은 해당 프로퍼티의 종류(저장/연산 프로퍼티)는 신경쓰지 않지만, 읽기 전용인지 읽기/쓰기 전용인지는 get과 set을 사용해 명시해야 한다.
프로토콜을 채택한 타입은 프로토콜이 요구하는 프로퍼티의 이름과 타입만 맞도록 구현하면 된다.
protocol FirstProtocol {
var name: String { get set }
var age: Int { get }
}
읽기/쓰기 전용일 때는 name과 같이, 읽기 전용일 때는 age와 같이 표현한다.
protocol AnotherProtocol {
static var someTypeProperty: Int { get set }
}
struct SomeStruct: FirstProtocol {
var name: String
var age: Int
}
이때 프로토콜을 준수하기 위해서는 프로토콜에 정의한 프로퍼티나 메서드와 같은 기능을 모두! 사용해야 한다. 그렇지 않으면 오류 발생함!
프로토콜은 특정 인스턴스 메서드나 타입 메서드를 요구할 수도 있다. 이때 메서드의 중괄호({}) 부분을 제외하고 메서드의 이름, 매개변수, 반환 타입 등만 작성한다. (가변 매개변수도 껴줌!)
추가로, 매개변수 기본 값은 지정할 수 없다.
protocol FirstProtocol {
var name: String { get set }
var age: Int { get }
func someTypeMethod()
}
struct SomeStruct: FirstProtocol {
var name: String
var age: Int
func someTypeMethod() {
print(name)
}
}
프로토콜은 프로퍼티, 메서드 등과 마찬가지로 특정한 이니셜라이저를 요구할 수도 있다. 프로토콜에서 이니셜라이저를 요구할 때, 메서드 요구와 마찬가지로 이니셜라이저를 정의하지만 구현을 하지 않는다.
protocol FirstProtocol {
var name: String { get set }
var age: Int { get }
func someTypeMethod()
init()
}
struct SomeStruct: FirstProtocol {
var name: String
var age: Int
func someTypeMethod() {
print(name)
}
init() {
}
}
class SomeClass: FirstProtocol {
var name: String
var age: Int
func someTypeMethod() {
print(name)
}
required init() {
}
}
하지만, 만약 클래스가 상속받을 수 없는 final 클래스라면 required 식별자를 붙여줄 필요가 없다.