본문 바로가기
IOS App Programming/Swift

[Swift] 프로토콜 - 프로토콜 구현, 채택, 클래스 전용 프로토콜

by B_Tori 2023. 4. 11.

프로토콜이란

프로토콜 : 특정 역할을 하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진을 정의하는 것

  • 정의해 둔 프로토콜을 실제 구조체 클래스 등에서 채택을 해 해당 요구사항을 정의한다.
    -> 이를 '해당 프로토콜을 준수한다'라고 표현
  • 프로토콜을 준수하기 위해서는 요구사항을 모두 구현해야 한다.
  • 프로토콜은 정의를 하고 요구사항을 제시만 할 뿐 스스로 기능을 구현하진 않는다.

프로토콜 구현

protocol 프로토콜이름{
	프로토콜 정의
}

프로토콜 채택

  • 타입 명: 프로토콜 이름 형태로 사용
  • 여러 개 채택 시 콤마로 구분 지어 사용
  • 만약 클래스에서 프로토콜을 채택할 때, 다른 클래스를 상속받는 클래스라면 상속받을 클래스 이름 다음에 채택할 프로토콜을 나열함
    ex) class A : 상속 클래스, 프로토콜 1, 프로토콜 2 

 

프로토콜 요구사항

프로퍼티 요구사항

  • 프로퍼티 종류(연산 프로퍼티인지 저장 프로퍼티인지)는 따로 신경 쓰지 않음
    종류 상관없이 요구하는 프로퍼티 이름과 타입만 맞추면 됨
  • 프로퍼티 요구는 항상 var 키워드를 사용해서 정의 (구현할 때는 상관 X)
  • 프로퍼티를 읽기 전용으로 할지, 읽고 쓰기가 모두 가능한지는 프로토콜이 정의해야 함
    읽기 전용 : 정의 뒤에 { get } 명시
    읽고 쓰기 모두 가능 : { get set }
    -> 단 요구사항에 읽기 전용이어도 읽고 쓰기가 모두 가능한 프로퍼티로 해도 상관없음 (정의한 기능이 존재하기만 하면 됨)
  • 타입 프로퍼티 요구 : static 키워드를 사용 -> 구현할 때는 class타입이든 static타입이든 상관 x

ex1) 프로포콜이 읽고 쓰기가 가능한 프로퍼티를 요구할 시

- 변수 프로퍼티로 구현 (변경이 가능해야 하므로)

- 읽기, 쓰기 가능한 연산 프로퍼티로 구현

ex2) 프로토콜이 읽기 전용 프로퍼티를 요구할 시

- 상수 프로퍼티, 읽기 전용 연산프로퍼티를 포함한 어떤 식의 프로퍼티로도 구현 가능
(프로퍼티 종류에 상관없고 읽기 쓰기 둘 다 가능해도 되기 때문)

메서드 요구사항

  • 메서드의 실제 구현부 { } 부분을 제외하고 메서드 이름, 매개변수, 반환 타입 등만 작성
  • 프로토콜 메서드 요구에서는 매개변수의 기본값을 지정할 수 없음
  • 타입 메서드 요구 시 static 키워드를 사용하며 메서드 또한 구현 시에는 class/ static 둘 다 사용 가능

이니셜 라이저 요구

  • 이니셜라이저 매개변수만 지정할 뿐 { }을 포함한 구현부는 작성하지 않음
  • 해당 이니셜라이저를 구현할 때는 required 키워드를 작성해야 함
    -> 클래스 자체가 상속을 받을 수 없는 final 클래스라면 required키워드 작성 안 해도 됨
  • 서브 클래스가 슈퍼클래스 생성자를 override 하고 프로토콜에서 요구하는 생성자도 구현하는 경우
    required와 override 키워드 모두 사용 (어느 것이 먼저 위치하든 상관없음)
  • 실패 가능한 이니셜라이저 요구 가능 -> 구현 시에는 일반 이니셜라이저로 구현해도 됨
// 프로토콜 구현
protocol Talkalbe{
    //프로퍼티 요구
    var topic : String { get set}
    var language : String { get }
    
    //메서드 요구
    func talk()
    
    //이니셜라이저 요구
    init(topic:String, language:String)
}


//프로토콜 채택 (클래스)
class Person:Talkalbe{
    var topic: String
    var language: String
    
    func talk() {
        print("\(topic)에 대해 \(language)로 말하는 중")
    }
    
    required init(topic: String, language: String){
        self.topic = topic
        self.language = language
    }
}

기본적인 프로토콜 사용 방법이다. 프로퍼티 요구, 메서드 요구, 이니셜라이저 요구를 확인할 수 있고

이를 채택한 Person클래스에서 모두 구현을 해준 모습이다.

 

프로토콜 상속

  • 하나이상의 프로토콜을 상속받아 더 많은 요구사항 추가 가능 (즉 다중 상속 가능)
  • 클래스 상속 문법과 유사 -> protocol 프로토콜 이름 : 상속 프로토콜 1, 상속 프로토콜{    }
  • A, B를 상속받은 프로토콜 C를 채택한 타입에서는 C의 요구사항뿐만 아니라 C가 상속받은 A와 B의 요구사항까지 구현해야 한다.

클래스 전용 프로토콜

프로토콜 상속 리스트 맨 처음에 class 키워드를 추가하면 클래스 타입만 채택할 수 있도록 제한을 둘 수 있음

// 상속
protocol Readable{
    func read()
}
protocol Writeable{
    func write()
}
protocol ReadWriteSpeakable : Readable, Writeable{
    func speak()
}

protocol ReadWriteSpeakable2 : class, Readable, Writeable{
    func speak()
}

struct A:ReadWriteSpeakable{
    func speak() {
        print("speak")
    }
    
    func read() {
        print("read")
    }
    
    func write() {
        print("write")
    }
}
//오류 발생
struct B:ReadWriteSpeakable2 {
    
}

구조체 A는 ReadWriteSpeakable프로토콜을 채택했다. 따라서 ReadWriteSpeakable의 요구사항인 speak()뿐만 아니라, 상속받은 클래스들의 요구사항인 write()와 read()까지 구현하였다.

ReadWriteSpeakable2를 채택한 구조체 B는 클래스 전용 프로토콜이라 오류가 발생한다. 구조체가 아닌 클래스로 채택하면 오류를 없앨 수 있다.

 

 

댓글