프로토콜을 통해 형식이 구현해야 하는 요구사항을 선언하고 이 요구사항을 충족하도록 형식을 구현하는 방법에 대해 공부
프로토콜 선언 문법과 프로토콜 채용 문법에 대해 공부
protocol ProtocolName { propertyRequirements methodRequirements initializerRequirements subscriptRequirements }
protocol ProtocolName: Protocol, ... { }
protocol Something {
func doSomething()
}
enum TypeName: ProtocolName, ... { }
struct TypeName: ProtocolName, ... { }
class TypeName: SuperClass, ProtocolName, ... { }
프로토콜 채용
struct Size: Something { // 상위 프로토콜 이름 선언
func doSomething() {
}
}
protocol ProtocolName: AnyObject { }
protocol SomethingObject: AnyObject, Something {
}
// 클래스 전용
class object: SomethingObject {
func doSomething() {
}
}
프로토콜에서 속성을 선언하고 형식에서 요구사항을 구현
protocol ProtocolName { var name: Type { get set } static var name: Type { get set } }
프로토콜 형식 선언
항상 var 키워드로 선언
protocol Figure {
static var name: String { get set }
}
struct Rectangle: Figure { // 상수 저장 속성 X
static var name = "Rect"
}
struct Tringle: Figure {
static var name = "Triangle"
}
class Circle: Figure { // class의 하위도 똑같이 class로 선언해야한다
class var name: String {
get {
return "Circle"
}
set {
}
}
}
프로토콜에서 메소드를 선언하고 형식에서 요구사항을 구현
protocol ProtocolName { func name(param) -> ReturnType static func name(param) -> ReturnType mutating func name(param) -> ReturnType }
- 프로토콜에서 메소드를 선언할 때 헤드만 작성
- 타입 메서드를 선언할 때는 func 키워드 앞에 static 키워드를 추가한다.
- 만약 프로트콜을 가평식에 추가할 수도 있고, 메소드 내부에서 속성값을 변경해야한다면 func 키워드 앞에 mutating 키워드를 추가해야한다.
- mutating 키워드로 선언되어 있다면 가평식 전용이라고 생각할 수 있다.
하지만 프로토콜에서 사용한 mutating 키워드는 메소드에서 속성을 변경할 수 있어야 한다는 요구사항을 표현
protocol Resettable {
static func reset()
}
class Size: Resettable { // struct에서만 입력을 받는 상위에 mutating를 추가해야 함수에서 오류 발생 x
var width = 0.0
var height = 0.0
func reset() {
width = 0.0
height = 0.0
}
class func reset() {
// static 대신 class를 선언하면 오버라이딩을 선언하는 동시에 프로토콜의 요구사항을 충족한다.
}
}
프로토콜에서 생성자를 선언하고 형식에서 요구사항을 구현
protocol ProtocolName { init(param) init?(param) init!(param) }
protocol Figure {
var name: String { get }
init(n: String)
}
// 구조체 선언 및 프로토콜 선언
struct Rectangle: Figure {
var name: String
// 프로토콜의 생성자를 바꾸면 밑에 생성자도 추가해야된다.
init(n: String) {
name = n
}
}
class Circle: Figure {
var name: String
required init(n: String) {
name = n
}
}
final class Triangle: Figure { // final = 더 이상 상속되지 않는 클래스
var name: String
// final은 더 이상 상속이 되지 않기 때문에 상속을 고려하지 않아도 된다.
// 그래서 required 키워드 없이 요구 사항을 충족시킴
// 프로토콜의 생성자를 바꾸면 밑에 생성자도 추가해야된다.
init(n: String) {
name = n
}
}
class Oval: Circle {
var prop: Int
init() {
prop = 0
super.init(n: "Oval")
}
required convenience init(n: String) {
self.init()
}
}
protocol Grayscale {
init?(white: Double)
}
struct Color: Grayscale {
init(white: Double) {
}
}
// 어렵다..
// 포기 금지
프로토콜에서 서브스크립트를 선언하고 형식에서 요구사항을 구현합니다.
protocol ProtocolName { subscript(param) -> ReturnType { get set } }
- Subscript에서 get으로 선언 했다고 해서 실제 구현에서 get 블럭만 구현해야 하는 것은 아니다.
- 값을 읽을 수 있어야 한다는 요구사항만 충족시키면 나머지 구현에는 제약이 없다
- get 키워드는 필수
protocol List {
subscript(idx: Int) -> Int { get }
}
struct DataStore: List {
subscript(idx: Int) -> Int {
get {
return 0
}
set {
}
}
}