프로토콜은 특정 작업이나 기능에 적합한 method, property 및 요구사항에 대한 청사진을 정의한다. 그런 다음 프로토콜은 class, structure 또는 enum에 의해 채택되어 그러한 요구사항의 실제 구현을 제공할 수 있다. 프로토콜의 요구사항을 만족하는 모든 형식은 해당 프로토콜에 적합하다고 한다.
반드시 구현해야 하는 요구사항을 지정하는 것 외에도, 이러한 요구사항의 일부를 구현하거나 추가 기능을 구현하기 위해 프로토콜을 확장할 수 있습니다.
클래스 상속과 같이 프로토콜 채택을 표현할 때는
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
//class definition goes here
}
프로퍼티가 get, set 요구한다면?
상수 저장 프로퍼티는 충족하지 못함
읽기 전용 연산 프로퍼티도 충족하지 못함
만약, 프로토콜이 오직 get만 요구하는 프로퍼티를 요구한다면?
어떤 종류의 프로퍼티가 와도 상관 없다.
그리고 get 뿐만 아니라 set도 구현해도 괜찮다
프로퍼티 요구사항은 무조건 변수 프로퍼티로 선언되어 진다.
타입 프로퍼티의 경우는 static을 명시해준다
프로토콜을 채택하고 충족하지 않으면 swift는 에러를 발생시킨다
protocol SomeProtocol {
static func someTypeMethod()
func random(number: Int) -> Double
}
enum 이나 structure 과 같은 값 타입들의 method들은 property를 변화시킬 경우 mutating 키워드를 사용한다. 프로토콜에서 이렇게 mutating method를 쓸 경우에는 mutating 키워드를 같이 표시해주면 된다.
But, 프로토콜에서 가변 메서드를 요구하지 않는다면, 값 타입의 인스턴스 내부 값을 변경하는 mutating 메서드는 구현이 불가능하다
프로토콜은 init 또한 요구할 수 있는데, method와 같은 형식으로 매개변수를 요구할 수 있으며, body가 없다. 단, 반환 값은 원래 init() 자체에 없다.
상속이 없는 struct의 경우는 init을 구현할 때 관계 없지만, class의 경우는 protocol에서 요구 받은 이니셜라이저를 구현할 때 상속을 생각하여 required 키워드를 꼭 써줘야 한다.
protocol SomeProtocol {
init(someProtocol: Int)
}
class SomeClass {
init(someProtocol: Int) { } // Error
required init(someProtocol: Int) { } // Correct
}
But, final class 의 경우는 다르다. final 자체가 상속을 하지 않는다는 것을 의미하기에 required를 init에 붙이지 않아도 된다
protocol SomeProtocol {
init(someProtocol: Int)
}
final class SomeClass {
init(someProtocol: Int) { } // Correct
}
그리고 프로토콜에서 요구하는 init이 상속을 받은 부모클래스에서 구현이 되어 있다면 그럴때는 required와 override를 같이 쓴다
protocol SomeProtocol {
init()
}
class SomeSuperClass {
init() {
// initializer implementation goes here
}
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
required override init() { }
}
실패 가능 이니셜라이저를 요구 받았을 경우, 실패 가능한 이니셜라이저 혹은 일반적인 이니셜라이저로 구현해도 상관 없다
protocol Named {
var name: String { get }
init?(name: String)
}
struct Animal: Named {
var name: String
init(name: String) {
self.name = name
}
}
struct Pet: Named {
var name: String
init?(name: String) {
self.name = name
}
}
class Person: Named {
var name: String
required init(name: String) {
self.name = name
}
}
class Student: Named {
var name: String
required init?(name: String) {
self.name = name
}
}
프로토콜도 클래스 처럼 서로 상속이 가능하다. 그리고 struct나 enum을 제외한 클래스을 상대로만 요구 프로토콜을 만들 수도 있다
protocol Readable {
func read()
}
protocol ReadSpeakable: Readable {
func speak()
}
class SomeClass: ReadSpeakable {
func read() {
}
func speak() {
}
}
protocol ClassOnlyProtocol: class {
}
struct SomeStruct: ClassOnlyProtocol {
//오류 : 클래스 전용 프로토콜은 struct에서 불가능
}
목표가 명확하면 무엇을 지금 당장 해야될지 보인다
→ SwiftUI롤 공부하겠어! 보다는 좀 더 작은 목표를 단기간에 세워서 빠르게 수행해라!
그리고 이 공부를 왜 해야되는지에 대한 근거가 생기면 좀 더 효과적이다! 왜 이 주제를 공부해야되는지 생각해보자!!
사전 조사
애플 공식문서를 보자... ⇒ iOS 공부에서 애플 공식문서는 시작과 끝이다!
학습을 시작하는 동시에 해당 주제에 대해서 기록하기 위해 블로그를 바로 켜자!
→ 애플 공식문서와 블로그를 동시에 키고, 학습과 기록을 동기화 시키자
피드백 퇴고를 하자!
→ 비록 본인이 작성했다고 할지라도 기록을 했다고 무조건 다 기억하는 것은 아니다. 주기적으로 퇴고를 하면서 배운 것에 대해서 remind하자!
애플 기본 계산기의 경우 연산자 우선순위가 높은 연산자의 계산을 미리 처리해서 화면에 보여주는 방식이다.
중간에 어떻게 처리해야 될까??
class로 구현 후 상속을 할지, protocol을 이용해서 채택해야할지 고민이다
⇒ 해결 방안, 이번 프로젝트에서 class 가 더 어울리는지, 혹은 protocol이 더 어울리는지 protocol의 개념을 완벽히 이해하고 적용해보자!!