본문으로 바로가기

프로토콜의 기본 문법

- 정의 -> 채택 -> 구현

- 정의

   - 프로토콜을 만들어 필요한 요구사항을 담음

- 채택

   - 프로토콜 채택 (클래스, 구조체, 열거형에서 채택가능)

- 구현

   - 프로토콜에서 요구하는 사항을 직접 구현

protocol SomeProtocol { // 프로토콜 정의
    func doSomething(num: Int) -> Int  // 메서드의 구현이 아닌 요구사항만 정의
//    func doSomething() { } (구현는 프로토콜을 채택한 곳에서 함)
}

class AClass: SomeProtocol { // 프로토콜 채택
    
    // 프로토콜 채택시 프로토콜의 요구사항을 반드시 구현해야함
    func doSomething(num: Int) -> Int {
        return num
    }
}

struct AStruct: SomeProtocol { // 구조체에서 채택
    
    // 프로토콜 채택시 프로토콜의 요구사항을 반드시 구현해야함
    func doSomething(num: Int) -> Int {
        return num
    }
}

enum AEnum: SomeProtocol { // 열거형에서 채택
    
    func doSomething(num: Int) -> Int {
        return 0
    }
}

프로토콜 요구사항 정의

- 속성의 요구사항

   - 최소한의 요구사항 지정

   - 저장속성/계산속성 모두 구현 가능 (프로토콜의 요구사항만으로는 저장/계산속성의 구별 불가능)

   - 인스턴스 속성 요구사항  

      - {get}          -> 저장속성: let / var

                        -> 계산속성: 읽기(get) / 읽기쓰기(get,set)

      - {get set}   -> 저장속성: var

                        -> 계산속성: 읽기쓰기(get,set)

protocol SomeProtocol { // 프로토콜 정의
    var num: Int { get } // 읽기 (최소한의 요구사항이기때문에 구현시 읽기쓰기 형식으로도 구현가능)
    var name: String { get set } // 읽기쓰기 (읽기만 구현 불가능)
    var age: Int { get }
    static var type: String { get set } // 타입속성
}

class AClass: SomeProtocol { // 프로토콜 채택
    
    // 프로토콜의 속성의 요구사항 구현
    let num = 10 // var으로도 구현가능
    var name = "Jimin" // let으로는 구현 불가능
    static var type = "Some Type"
    
    // 프로토콜의 정의에서는 속성이 계산속성인지 저장속성인지 구별 불가능
    // 즉, 구현시 계산속성으로 구현도 쌉가능
    var age: Int {
        return 24
    }
}

- 메서드의 요구사항 정의

   - 메서드의 헤드부분(인풋, 아웃풋)까지만 정의

   - 구조체에서 저장속성 변경하는경우 mutating 키워드 붙임 (구조체로 제한하는것은 아님)

protocol SomeProtocol { // 프로토콜 정의
    func randomNum() -> Int // 메서드의 헤드부분(인풋, 아웃풋)까지만 정의
    mutating func changeNum(_ num: Int) // 구조체에서 저장속성 변경시 mutating키워드 붙임
    static func doSomething() // 타입속성
}

class AClass: SomeProtocol {
    var num = 0
    
    func randomNum() -> Int {
        return Int.random(in: 1...100)
    }
    
    // 프로토콜 정의 부분에 mutating 키워드가 있어도 클래스에서 구현 가능
    func changeNum(_ num: Int) {
        self.num = num
    }
    
    static func doSomething() {
        print("doSomething")
    }
}

struct AStruct: SomeProtocol {
    
    var num = 0
    
    func randomNum() -> Int {
//        self.num = 10 (프로토콜 정의에 mutating 키워드 없으면 저장속성 변경 불가)
        return Int.random(in: 1...100)
    }
    
    mutating func changeNum(_ num: Int) {
        self.num = num
    }
    
    static func doSomething() {
        print("doSomething")
    }
}

프로토콜은 확장에서 구현하는게 국룰임

protocol SomeProtocol { // 프로토콜 정의
    func randomNum() -> Int // 메서드의 헤드부분(인풋, 아웃풋)까지만 정의
    mutating func changeNum(_ num: Int) // 구조체에서 저장속성 변경시 mutating키워드 붙임
    static func doSomething() // 타입속성
}

class AClass {
    var num = 0
}

// 프로토콜은 확장(extension)에서 구현하는게 일반적이다
extension AClass: SomeProtocol {
    func randomNum() -> Int {
        return Int.random(in: 1...100)
    }
    
    // 프로토콜 정의 부분에 mutating 키워드가 있어도 클래스에서 구현 가능
    func changeNum(_ num: Int) {
        self.num = num
    }
    
    static func doSomething() {
        print("doSomething")
    }
}

상속과 프로토콜 채택을 같이할때는 상속먼저

protocol SomeProtocol { } // 프로토콜 정의

class SomeClass { }

// 상속과 프로토콜 채택을 같이할때는 상속먼저
class AClass: SomeClass, SomeProtocol { }

프로토콜은 다중채택 쌉가능

protocol SomeProtocol { }
protocol SomeProtocol2 { }

// 프로토콜은 다중채택 가능
class AClass: SomeProtocol, SomeProtocol2 { }

프로토콜의 선택적 구현

// 프로토콜 앞에 @objc 키워드 붙이기
@objc protocol Remote {
    // 선택적으로 구현을 하고싶은 요구사항 앞에 @objc optional 키워드 붙이기
    @objc optional func turnOn()
    func turnOff()
}

class AClass: Remote {
    func turnOff() {
        print("꺼짐")
    }
    
    /*
     @objc optional 키워드가 붙으면 구현을 선택적으로 해도 된다
     
    func turnOn() {
        
    }
     */
}