본문 바로가기
Swift/The Swift Programming Language

[Swift] Protocols(프로토콜) - 5

by Kellang 2023. 7. 4.

Protocol Extensions

프로토콜은 메소드, 이니셜라이저, 서브스크립트, 컴퓨티드 프로퍼티를 해당 프로퍼티를 준수하는 타입들에게 제공하도록 확장될 수 있다. 이는 각 타입의 개별 준수나 전역 함수가 아니라 프로토콜 스스로 동작을 정의할 수 있게 해준다.

 

예를 들어, RandomNumberGenerator 프로토콜은 random() 메소드 요구사항의 결과값을 사용하여 랜덤 Bool 값을 리턴하는 randomBool() 메소드를 제공하도록 확장될 수 있다.

extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
}

프로토콜의 익스텐션을 만들면, 해당 프로토콜을 준수하는 모든 타입은 추가적인 수정 없이 모든 이러한 메소드 구현을 자동적으로 얻게 된다.

let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Prints "Here's a random number: 0.3746499199817101"
print("And here's a random Boolean: \(generator.randomBool())")
// Prints "And here's a random Boolean: true"

프로토콜 익스텐션은 해당 프로토콜을 준수하는 타입에게 구현을 추가할 수 있만, 프로토콜을 확장하거나 다른 프로토콜에서 상속을 받지는 못한다. 프로토콜 상속은 항상 프로토콜의 선언에서만 지정된다.

 

Providing Default Implementations

프로토콜 확장을 이용하여 메소드나 컴퓨티드 프로퍼티 요구사항의 디폴트 구현을 제공할 수 있다. 만약 해당 프로토콜을 준수하는 타입이 메소드나 프로퍼티의 구현을 제공한다면 해당 구현을 익스텐션에서 제공하는 디폴트 구현 대신 사용하게 된다.

Note
익스텐션에서 기본 구현이 제공되는 프로토콜 요구사항은 옵셔널 프로토콜 요구사항과 구분된다. 둘 모두 해당 프로토콜을 준수하는 타입에서 꼭 구현할 필요는 없지만, 디폴트 구현이 있는 요구사항은 옵셔널 체이닝 없이도 호출 가능하다.

예를 들어 TextRepresentable 프로토콜을 상속받는 PrettyTextRepresentable 프로토콜은 prettyTextualDescription 프로퍼티 요구사항의 디폴트 구현을 textualDescription 프로퍼티의 결과를 리턴함으로써 간단하게 제공할 수 있다:

extension PrettyTextRepresentable  {
    var prettyTextualDescription: String {
        return textualDescription
    }
}

 

Adding Constraints to Protocol Extensions

프로토콜 익스텐션을 정의할 때, 해당 프로토콜을 준수하는 타입들이 익스텐션의 메소드와 프로퍼티들을 사용하기 위해 반드시 만족해야 할 제약조건을 지정할 수 있다. 이 제약조건들을 확장하려는 프로토콜의 이름 뒤에 제네릭 where 절을 이용하여 작성한다. 제네릭 where 절에 대한 정보는 Generic Where Clauses에 나와있다.

 

예를 들어 모든 원소가 Equatable 프로토콜을 준수하는 컬렉션에 적용될 Collection 프로토콜의 익스텐션을 정의할 수 있다. 컬렉션의 원소가 Equatable 프로토콜을 준수하도록 제약조건은 걸면, 각 원소들이 같은지 다른지 확인할 수 있다.

extension Collection where Element: Equatable {
    func allEqual() -> Bool {
        for element in self {
            if element != self.first {
                return false
            }
        }
        return true
    }
}

allEqual() 메소드는 컬렉션 내부의 모든 원소들이 같다면 true를 리턴한다.

 

하나는 모든 원소들이 같고 다른 하나는 그렇지 않은, 두 개의 정수 배열을 생각해보자:

let equalNumbers = [100, 100, 100, 100, 100]
let differentNumbers = [100, 100, 200, 100, 200]

배열은 Collection을 준수하고 Int 타입은 Equatable을 준수하므로, 위의 두 배열은 allEqual() 메소드를 사용할 수 있다:

print(equalNumbers.allEqual())
// Prints "true"
print(differentNumbers.allEqual())
// Prints "false"
Note
만약 프로토콜을 준수하는 타입이 같은 메소드나 프로퍼티를 구현하는 다른 제약조건 익스텐션을 여러 개 만족할 때, 스위프트는 가장 상세한 제약조건에 있는 구현을 사용하도록 한다.

 

 

원문: https://books.apple.com/kr/book/the-swift-programming-language-swift-5-7

 

‎The Swift Programming Language (Swift 5.7)

‎Computing & Internet · 2014

books.apple.com

댓글