728x90
반응형

프로토콜 (Protocol)

프로토콜은 특정 역할을 수행하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진을 정의한다.
구조체, 클래스, 열거형은 프로토콜을 채택 해서 특정 기능을 수행하기 위한 프고토콜의 요구사항을 실제로 구현할 수 있다.
어떤 프로토콜의 요구사항을 모두 따르는 타입은 그 프로토콜을 준수한다 라고 표현한다.
타입에서 프로토콜의 요구사항을 충족시키려면 프로토콜이 제시하는 청사진의 기능을 모두 구현해야 한다.
즉, 프로토콜은 기능을 정의하고 제시할 뿐, 스스로 기능을 구현하지 않는다.

프로토콜 정의

프로토콜은 다음과 같이 protocol 키워드를 사용해서 정의할 수 있다.

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

그리고 다음과 같이 프로퍼티, 메서드, 이니셜라이저 등의 요구사항을 정의한다.

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

주석에도 설명이 되어 있지만
프로퍼티 요구는 항상 var 키워드를 사용한다.
get은 읽기만 가능해도 상관없다는 뜻이고
get과 set을 모두 명시하면 읽기와 쓰기가 모두 가능한 프로퍼티여야한다는 것이다.

프토코콜 채택 및 프로토콜 요구 구현

구조체와 클래스 그리고 열거형은 프로토콜을 채택해서 사용할 수 있다.

struct Person: Talkable {

}

Person 이라는 구조체는 Talkable 이라는 프로토콜을 채택했다. 라고 할 수 있다.
"타입명: 프로토콜 이름" 와 같이 프로토콜을 채택할 수 있다. 클래스를 상속받을 때와 비슷하게 사용한다.

프로토콜을 채택하면 프로토콜에서 정의한 요구사항들을 구현해야한다.
우선 프로토콜에서 정의한 프로퍼티 요구사항을 구현해준다.

struct Person: Talkable {
    // 프로토콜에서 정의한 프로퍼티 요구
    // 저장 프로퍼티로 구현
    var topic: String
    var language: String
}

 

위의 프로퍼티 요구의 경우 저장 프로퍼티로 구현했지만
프로퍼티 요구는 다양한 방법으로 해석해 구현할 수 있다.
읽기 전용 프로퍼티 요구는 연산 프로퍼티로 대체할 수 있다.
읽기, 쓰기 프로퍼티도 마찬가지로 연산 프로퍼티로 대체할 수 있다.

struct Person: Talkable {
    // 읽기 전용 프로퍼티 요구는 연산 프로퍼티로 대체 가능
    var language: String { return "한국어" }
    
    // 읽기 쓰기 프로퍼티도 연산 프로퍼티로 대체할 수 있다.
	var subject: String = ""
	var topic: String {
        set {
            self.subject = newValue
        }
        get {
            return self.subject
        }
    }
}

프로퍼티 요구를 구현했으면 메서드 요구와 이니셜라이저 요구도 구현해준다.

// Person 구조체는 Talkale 프로토콜을 채택했다.
struct Person: Talkable {
    // 읽기 전용 프로퍼티 요구는 연산 프로퍼티로 대체 가능
    var language: 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
    }
}

프로토콜에서 정의항 요구사항들을 모두 구현했고 프로퍼티 요구의 경우 연산 프로퍼티를 통해 구현해보았다.

프로토콜 상속

프로토콜은 하나 이상의 프로토콜을 상속받아 기존 프로토콜의 요구사항보다 더 많은 요구사항을 추가할 수 있다.
프로토콜 상속 문법은 클래스의 상속문법과 비슷하지만 프로토콜은 클래스와 다르게 다중 상속이 가능하다.

protocol [프로토콜 이름]: [부모 프로토콜 이름 1], [부모 프로토콜 이름 2], ... {
	/* 프로토콜에서 정의한 요구사항 구현*/
}

그럼 예를 들어, 부모 프로토콜과 부모 프로토콜을 상속받은 프로토콜을 생성해보았다.

// 부모 프로토콜 정의

protocol Read {
    func read()
}

protocol Write {
    func write()
}

// 부모 프로토콜을 상속받은 프로토콜

protocol ReadSpeak: Read {
    func speak()
}

protocol ReadWriteSpeak: Read, Write {
    func speak()
}

상속에 대해서 알아보려고 생성한 프로토콜이기 때문에 메서드 요구만 정의해주었다.

그리고 프로토콜을 상속받은 클래스를 정의해주었다.

class SuperClass: Read {
    func read() {
        print("super class Read")
    }
}

// 클래스 상속한 후에 프로토콜을 상속받아야 한다.
class SubClass: SuperClass, Write, ReadSpeak {
    func write() {
        print("sub class write")
    }
    func speak() {
        print("sub class speak")
    }
}

수퍼클래스의 경우 Read 프로토콜만 상속받았고,
서브클래스의 경우 SuperClass 클래스를 상속 받은 후에 Write, ReadSpeak 프로토콜을 상속 받았다.

서브 클래스의 경우 클래스와 프로토콜을 상속받았는데
여기서 주의해야할 부분은 클래스를 먼저 상속받은 후에 프토코콜을 상속받아야 한다는 점이다.

그리고 인스턴스를 생성해 특정 프로토콜을 준수하는지 확인할 수 있다.

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

var someAny: Any = sup
someAny is Read      // true
someAny is ReadSpeak // false

someAny = sub
someAny is Read      // true
someAny is ReadSpeak // true

is, as 연산자를 통해 타입을 확인해보았다.

그리고 다운 캐스팅을 통해 동작 여부도 확인해보았다.

someAny = sup

if let someRead: Read = someAny as? Read {
    someRead.read()
}

// 출력 결과 : super class Read



if let someReadSpeak: ReadSpeak = someAny as? ReadSpeak {
    someReadSpeak.speak()
    // 수퍼클래스는 ReadSpeak를 채택하지 않았기 때문에 동작하지 않는다.
}

// 출력 결과 : 동작하지 않음


someAny = sub

if let someRead: Read = someAny as? Read {
    someRead.read()
}

// 출력 결과 : super class Read

 

 

- 참고 사이트 -

www.edwith.org/boostcamp_ios/lecture/11320

 

[LECTURE] 26. 프로토콜 (💎생각해보기) : edwith

생각해보기   학부에서 Java 등 다른언어를 많이 배워서 그런지 스위프트의 프로토콜을 다른언어의 추상 클래스 혹은 인터페이스와 동치하여 표현하는 경우를 많이 보았습니다. 안타깝습... - 부

www.edwith.org

medium.com/@jgj455/오늘의-swift-상식-protocol-f18c82571dad

 

오늘의 Swift 상식 (Protocol)

프로토콜이란?

medium.com

 

728x90
반응형
복사했습니다!