본문 바로가기
etc/Swift

프로토콜(Protocol)

by IT learning 2021. 5. 28.
728x90

 

오늘은 프로토콜에 대해 공부했다.

 

프로토콜은 우리가 잘 알고있는 단어의 뜻인 '규약'과 관련이 있다.

프로토콜은 특정 역할을 수행하기 위한 메서드, 프로퍼티, 이니셜라이저 등의 요구사항을 정의한다.

 

구조체, 클래스, 열거형(enum)은 프로토콜을 채택(약간 상속느낌이다)해서 프로토콜의 요구사항을 실제로 구현할 수 있다.

어떤 프로토콜의 요구사항을 모두 따르는 타입은 ' 프로토콜을 준수한다' 고 표현한다.

프로토콜의 요구사항을 충족시키려면 프로토콜이 제사히는 기능을 모두 구현해야 한다. 이미 채택한 것들은 무조건 구현해야 컴파일 에러가 안난다.

 

정의

 protocol 프로토콜 이름 {
    /* 정의부 */
 }

위와 같은 형식으로 구현한다.

protocol Talkable {
    // 프로퍼티 요구
    // 프로퍼티 요구는 항상 var 키워드를 사용합니다
    // get은 읽기만 가능해도 상관 없다는 뜻이며
    // get과 set을 모두 명시하면
    // 읽기 쓰기 모두 가능한 프로퍼티여야 합니다
    var topic: String { get set }
    var language: String { get }
    
    // 메서드 요구
    func talk()
    
    // 이니셜라이저 요구
    init(topic: String, language: String)
}

위의 코드는 프로토콜을 만들어본 코드이다.  말하기 기능을 가진 프로토콜인데,  프로퍼티도 요구할 수 있고, 메서드, 이니셜라이저까지 가능하다. 프로퍼티에 get,set이 보일텐데 각자 읽기와 쓰기가 가능한 키워드들이다. 둘 다 있으면 읽기쓰기 다 가능한 프로퍼티이다.

그리고 각 프로퍼티가 읽기, 쓰기, 읽기와 쓰기에 맞는 프로퍼티여아 한다.  var = get,set 다 가능하고 let = get만 가능한것처럼 ㅇㅇ

그리고 나머지 메서드, 이니셜라이저는 저 형식대로 무조건 구현해야 한다. 아니면 컴파일 안된다.

 

프로토콜 채택 및 준수

// Person 구조체는 Talkable 프로토콜을 채택했습니다
struct Person: Talkable {
    var topic: String    // 읽기,쓰기 전용
    let language: String // 읽기 전용
    
    // 읽기전용 프로퍼티 요구는 연산 프로퍼티로 대체가 가능합니다
//     var lenguage: String { return "한국어" }
    
    // 물론 읽기, 쓰기 프로퍼티도 연산 프로퍼티로 대체할 수 있습니다
//    var subject: String = ""
//    var topic: String {
//        set {
//            self.subject = newValue
//        }
//        get {
//            return self.subject
//        }
//    }
    
    func talk() {
        print("\(topic)에 대해 \(language)로 말합니다")
    }
    
    init(topic: String, language: String) {
        self.topic = topic
        self.language = language
    }
}

위 코드를 보자. 구조체 Person은 Talkable 프로토콜을 채택했다. 그리고 각 프로퍼티들을 소환했고, 각자 읽기쓰기, 읽기 전용이다.

그런데 프로퍼티 요구는 바꿀수가 있다. 아까 읽기전용 프로퍼티는 연산 프로퍼티로 대체가 가능하다. 연산 프로퍼티에서 get set 둘다 구현해놓으면 둘 다 사용이 가능한 프로퍼티로 바뀐다. 주석이 그 예다

 

그리고 아래는 아까 메서드, 이니셜라이저 를 규약대로 구현해놓은 코드이다.

 

 

프로토콜 상속

프로토콜은 클래스와 다르게 다중상속이 가능하다

정의

 protocol 프로토콜 이름: 부모 프로토콜 이름 목록 {
 /* 정의부 */
 }

위와 같은 형식으로 구현하면 된다.

protocol Readable {
    func read()
}

protocol Writeable {
    func write()
}

protocol ReadSpeakable: Readable {
    //func read()
    func speak()
}

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

struct SomeType: ReadWriteSpeakable {
    func read() {
        print("Read")
    }
    func write() {
        print("Write")
    }
    func speak() {
        print("Speak")
    }
}

위 코드와 같이 프로토콜은 다중상속이 가능하다.

 

클래스 상속과 프로토콜

class SuperClass: Readable {
    func read() { print("read") }
}

class SubClass: SuperClass, Writeable, ReadSpeakable {
    func write() { print("write") }
    func speak() { print("speak") }
}

클래스에서 상속과 프로토콜 채택을 동시에 하려면 상속받으려는 클래스를 먼저 명시하고 그 뒤에 채택할 프로토콜 목록을 작성한다.

 

프로토콜 준수 확인

let sup: SuperClass = SuperClass()
let sub: SubClass = SubClass()


var someAny: Any = sup
someAny is Readable // true
someAny is ReadSpeakable // false

someAny = sub
someAny is Readable // true
someAny is ReadSpeakable // true

someAny = sup

if let someReadable: Readable = someAny as? Readable {
    someReadable.read()
} // read

if let someReadSpeakable: ReadSpeakable = someAny as? ReadSpeakable {
    someReadSpeakable.speak()
} // 동작하지 않음

someAny = sub

if let someReadable: Readable = someAny as? Readable {
    someReadable.read()
} // read

인스턴스가 특정 프로토콜을 준수하는지 확인할 수 있다.

아까 본 타입 캐스팅과 같이 is, as 로 확인이 가능하다.

 

그리고 if let 구문과 as 옵셔널로 조건부 구문을 구현 할 수 있다.

 

이게 말이나 되는가? 예시 작성하는데 무슨 200줄 가까이 나오는가..미치겠다..

728x90

'etc > Swift' 카테고리의 다른 글

오류 처리  (0) 2021.05.28
익스텐션(Extension)  (0) 2021.05.28
assert와 guard  (0) 2021.05.28
타입 캐스팅(Type Casting)  (0) 2021.05.28
옵셔널 체이닝(부제 - 갈고리 살인마)  (0) 2021.05.28

댓글

IT_learning's Commit