미루고 미뤘던 protocol에 대해 공부해보려고 합니다.
Swift의 꽃이 무엇이냐고 하면 optional이랑 protocol을 말하는데 optional에 대해서 포스팅했으니 protocol 차례인거 같네요.
프로토콜 포스팅할 떄 Zeddios0202님의 블로그를 참고했습니다. (제 iOS, Swift 좋은 스승님들 중 한 분입니다 ㅎ)
프로토콜은 특정 작언이나 기능에 적합한 메소드, 프로퍼티 및 기타 요구사항의 청사진(Blueprint)을 의미합니다. 클래스, 구조체 열거형이 프로토콜을 채택할 수 있으며 채택하면 프로토콜에서 사전에 정의된 요구사항을 실제로 구현해야합니다. 프로토콜의 요구 사랑을 모두 충족하는 모든 유형은 해당 프로토콜에 부합하다고 합니다.
많이 어렵나요? 정의는 해당하는 모든 의미를 담아야하기 때문에 어려울 수 있습니다. 프로토콜을 간단히 말하면 규약, 약속이라고 할 수 있습니다.
프로토콜을 통해서 어떤 프로퍼티가 필요하고 어떤 메서드가 필요한지 요구사항을 선언하는 것입니다. 여기서 핵심은 프로토콜은 필요한 메소드 및 프로퍼티를 선언만 하며 실제 구현하지 않습니다. 해당 프로토콜을 채택한 유형에서 "구현"한다는 것!!!!!!!!!
protocol SomeProtocol {
// protocol definition goes here
}
프로토콜은 해당 프로토콜을 준수하는 타입에게 특정 이름과 타입인, 인스턴스 프로퍼티 또는 타입 프로퍼티나 타입 프로퍼티를 요구할 수 있습니다. 프로토콜은 해당 프로퍼티가 저장인지 연산인지 명시하지 않습니다. 오직 프로퍼티의 이름과 타입만이 요구됩니다.
프로토콜은 각 프로퍼티에 gettable인지 gettable/settable인지 명시를 꼭 해야합니다.
프로퍼티는 항상 var 키워드와 함께 변수로 선언되어야 합니다.
gettable과 settable 프로퍼티는 선언 다음에 {get set}을 쓰고, gettable 프로퍼티는 {get}을 씁니다.
아래 예시에서 프로퍼티 요구사항이 var 키워드를 사용해서 정의된걸 확인할 수 있습니다. 또한 get, set 키워드를 활용하여 gettable인지 gettable/settable인지 명시한것을 알 수 있습니다. type property 또한 protocol 안에서 사용될 수 있음을 확인할 수 있습니다.
protocol SomeProtocol {
var mustBeSettable: Int { get set } // gettable/settable
var doesNotNeedToBeSettable: Int { get } // gettable
static var someTypeProperty: Int { get set } // 타입 프로퍼티
}
프로퍼티 요구사항을 포함하고 있는 프로토콜 예제를 활용하여 이를 채택하고 어떻게 준수하는지에 대해서 알아봅시다.
protocol FullyNamed {
var fullName: String { get }
}
FullyNamed은 fullName이라는 프로퍼티 요구사항을 가지는 프로토콜입니다.
해당 프로토콜을 채택한 유형이 해당 요구사항을 어떻게 준수하는지 알아봅시다.
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var fullName: String
}
FullyNamed 프로토콜을 채택한 구조체 Person에서 프로퍼티를 구현한 것을 알 수 있습니다. 위에서 '프로토콜은 이 프로퍼티가 저장 프로퍼티인지 연산 프로퍼티인지 명시하지 않고 오직 프로퍼티의 이름과 타입만이 요구됩니다.' 라고 설명했습니다. 오직 이름, 타입에 대한 약속 내용 만 지키면 된다는 것을 의미한답니다!
다음은 gettable, settable에 대한 내용입니다.
protocol FullyNamed {
var fullName: String { get }
}
프로토콜에서 프로퍼티 요구사항이 gettable만 필요한 경우 모든 종류의 프로퍼티에서 요구사항을 충족시킬 수 있다. 이 의미는 gettable만 요구하면 이 저장프로퍼티로 선언하든 연산프로퍼티로 선언하든 상관없다는 내용입니다.
1. 저장 프로퍼티 - 상수, 변수
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
let fullName: String
}
var john = Person(fullName: "John Appleseed")
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var fullName: String
}
var john = Person(fullName: "John Appleseed")
john.fullName = "Tim"
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var name: String
var fullName: String {
return name
}
}
var john = Person(name: "John Appleseed")
john.fullName = "Tim" //error! 'fullName' is a get-only property
fullName은 프로토콜에서 읽기전용이었으므로 get만 구현해야하며 fullName을 통해 다른 값으로 변경할 수 없습니다. 연산 프로퍼티는 값을 저장하는게 아닌 연산을 통해서 값을 세팅하거나 리턴하는 것이므로 인스턴스를 초기화할 때 연산프로퍼티인 fullName을 통해 초기화할 수 없고 진짜 값이 저장되는 name을 사용해야합니다.
다시 한번 복습,
-> 프로토콜에서 gettable만 필요로 하는 경우, 모든 종류의 프로퍼티에서 요구사항을 충족시킬 수 있으며 필요하다면 settable이 될 수 있습니다. (settable을 추가할 수 있다.)
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var name: String
var fullName: String {
get {
return name
}
set {
name = newValue
}
}
}
var john = Person(name: "John Appleseed")
john.fullName = "Zedd"
프로토콜에서는 {get}으로만 요구했는데 필요하다면 settable을 할 수 있다는 뜻입니다.
그럼! 만약 프로토콜이 gettable/settable 프로퍼티를 요구하면 어떻게 될까요?
gettable/settable로 요구하면 상수 저장 프로퍼티 또는 읽기 전용 연산 프로퍼티로 충족되서는 안됩니다.
잘 생각해보면 당연합니다! 읽기와 쓰기가 요구사항인데 상수 저장 프로퍼티나 읽기 전용 연산 프로퍼티로 선언되면 쓰기가 안되고 읽기만 가능하니까 안되는게 당연합니다. 예제를 통해서 체화시켜봅시다!
protocol FullyNamed {
var fullName: String { get set }
}
struct Person: FullyNamed {
let fullName: String//error! error: type 'Person' does not conform to protocol 'FullyNamed'
}
protocol FullyNamed {
var fullName: String { get set }
}
struct Person: FullyNamed {
var name: String
var fullName: String {
return name
}//error! error: type 'Person' does not conform to protocol 'FullyNamed'
}
프로토콜의 프로퍼티 요구사항에 대해서 알아봤습니다. 좀 많이 어려웠네요!
프로토콜은 많이 보기만 했지 직접 작성해본적은 없는데 메소드 요구사항까지 공부하고 프로토콜을 잘 활용할 수 있는 실력이 되도록 하겠습니당!!!!!