Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Archives
Today
Total
관리 메뉴

주니곰의 괴발노트

Swift - Protocol(준수에 대한 검사, 상속, 옵셔널 프로토콜, 확장) (4/4) 본문

iOS

Swift - Protocol(준수에 대한 검사, 상속, 옵셔널 프로토콜, 확장) (4/4)

Jhe2 2023. 2. 19. 16:49

확장으로 프로토콜 준수성 추가

  • 기존 타입에 대해 소스 코드에서 접근할 수 없지만 새로운 프로토콜을 채택하고 준수하기 위해 기존 타입을 확장가능
  • 확장은 기존 타입에 새로운 프로퍼티, 메서드, 그리고 서브 스크립트를 추가할 수 있으므로 프로토콜이 요구할 수 있는 모든 요구사항추가 가능
  • 타입의 기존 인스턴스는 확장에 인스턴스의 타입이 추가될 때 자동으로 프로토콜을 채택하고 준수

 

swift
닫기
protocol TextRepresentable { ​​​​var textualDescription: String { get } } extension Dice: TextRepresentable { ​​​​var textualDescription: String { ​​​​​​​​return "A \(sides)-sided dice" ​​​​} } let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator()) print(d12.textualDescription) // Prints "A 12-sided dice" extension SnakesAndLadders: TextRepresentable { ​​​​var textualDescription: String { ​​​​​​​​return "A game of Snakes and Ladders with \(finalSquare) squares" ​​​​} } print(game.textualDescription) // Prints "A game of Snakes and Ladders with 25 squares"
  • 위에서와 같이 Dice 를 확장한 후 TextRepresentable 채택하고 준수
  • 프로토콜 이름은 콜론으로 구분된 타입 이름 뒤에 채택하고 프로토콜의 모든 요구사항은 확장의 중괄호 내에 구현
  • 이제 모든 Dice 인스턴스를 TextRepresentable 로 처리가능
  • 마찬가지로 SnakesAndLadders 게임 클래스는 TextRepresentable 프로토콜을 채택하고 준수하기 위해 확장가능

 

조건적으로 프로토콜 준수

swift
닫기
extension Array: TextRepresentable where Element: TextRepresentable { ​​​​var textualDescription: String { ​​​​​​​​let itemsAsText = self.map { $0.textualDescription } ​​​​​​​​return "[" + itemsAsText.joined(separator: ", ") + "]" ​​​​} } let myDice = [d6, d12] print(myDice.textualDescription) // Prints "[A 6-sided dice, A 12-sided dice]"
  • 타입을 확장할 때 제약조건을 나열하여 일반 타입이 프로토콜을 조건적으로 준수할 수 있도록 만들 수 있음
  • 일반적인 where 절을 작성하여 채택 중인 프로토콜의 이름 뒤에 제약조건을 작성

 

확장과 함께 프로토콜의 채택 선언

swift
닫기
struct Hamster { ​​​​var name: String ​​​​var textualDescription: String { ​​​​​​​​return "A hamster named \(name)" ​​​​} } extension Hamster: TextRepresentable {} let simonTheHamster = Hamster(name: "Simon") let somethingTextRepresentable: TextRepresentable = simonTheHamster print(somethingTextRepresentable.textualDescription) // Prints "A hamster named Simon"

 

  • 타입이 이미 프로토콜의 모든 요구사항을 준수하지만 해당 프로토콜을 책택한다고 아직 명시하지 않은 경우 빈 확장을 사용하여 프로토콜을 채택하도록 만들 수 있음
  • Hamster 의 인스턴스는 TextRepresentable 이 요구된 타입 어디서든 사용될 수 있음
  • 요구사항이 충족된다고 해서 프로토콜을 자동으로 채택하지 않으므로 항상 프로토콜 채택을 명시적으로 선언해야 함

 

합성된 구현을 사용하여 프로토콜 채택

 

  • Swift는 많은 경우에 Equatable, Hashable, 그리고 Comparable 에 대해 프로토콜 준수성을 자동으로 제공할 수 있음
  • 합성된 구현을 사용하면 프로토콜 요구사항 구현을 위해 반복적인 상용구 코드를 작성할 필요가 없음
  • Swift는 다음과 같은 사용자 정의 타입에 대해 Equatable 의 합성된 구현을 제공
    • Equatable 프로토콜을 준수하는 저장된 프로토콜만 있는 구조체
    • Equatable 프로토콜을 준수하는 연관된 타입만 있는 열거형
    • 연관된 타입이 없는 열거형

 

swift
닫기
struct Vector3D: Equatable { ​​​​var x = 0.0, y = 0.0, z = 0.0 } let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0) let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0) if twoThreeFour == anotherTwoThreeFour { ​​​​print("These two vectors are also equivalent.") } // Prints "These two vectors are also equivalent."
  • == 의 합성된 구현을 받기 위해선 == 연산자를 직접 구현하지 않고 원래 선언을 포함한 파일에서 Equatable 에 대한 준수성을 선언
  • Equatable 프로토콜은 != 의 기본 구현을 제공
  • Vector2D 구조체와 유사한 3차원의 벡터 (x, y, z) 에 대한 Vector3D 구조체를 정의
  • xy, 그리고 z 프로퍼티는 모두 Equatable 타입이므로 Vector3D 는 등가 연산자의 합성된 구현을 받음

 

swift
닫기
enum SkillLevel: Comparable { ​​​​case beginner ​​​​case intermediate ​​​​case expert(stars: Int) } var levels = [SkillLevel.intermediate, SkillLevel.beginner, ​​​​​​​​​​​​​​SkillLevel.expert(stars: 5), SkillLevel.expert(stars: 3)] for level in levels.sorted() { ​​​​print(level) } // Prints "beginner" // Prints "intermediate" // Prints "expert(stars: 3)" // Prints "expert(stars: 5)"

 

  • Swift는 아래와 같은 사용자 정의 타입에 대해 Hashable 에 합성된 구현을 제공
    • Hashable 프로토콜을 준수하는 저장된 프로퍼티만 가지는 구조체
    • Hashable 프로토콜을 준수하는 연관된 타입만 가지는 열거형
    • 연관된 타입이 없는 열거형
  • hash(into:) 에 합성된 구현을 받기 위해선 hash(into:) 메서드를 직접 구현하지 않고 원래 선언을 포함한 파일에서 Hashable 에 대한 준수성을 선언
  • Swift는 원시값이 없는 열거형에 대해 Comparable 에 합성된 구현을 제공
  • 열거형이 연관된 타입을 가지고 있다면 모두 Comparable 프로토콜을 준수해야 함
  • < 의 합성된 구현을 받기 위해선 < 연산자를 직접 구현하지 않고 원래 열거형 선언을 포함한 파일에서 Comparable 에 대한 준수성을 선언
  • <=>, 그리고 >= 의 Comparable 프로토콜의 기본 구현은 나머지 비교 연산자를 제공

 

프로토콜 타입의 콜렉션

  • 프로토콜은 배열 또는 딕셔너리와 같은 콜렉션에 저장되기 위해 타입으로 사용될 수 있음

 

 

swift
닫기
let things: [TextRepresentable] = [game, d12, simonTheHamster] for thing in things { ​​​​print(thing.textualDescription) } // A game of Snakes and Ladders with 25 squares // A 12-sided dice // A hamster named Simon

 

  • 이제 배열에 해당타입의 프로퍼티를 넣고 반복문을 통해 각 항목의 설명을 출력
  • thing 상수는 TextRepresentable 타입
  • 실제 인스턴스가 Dice 또는 DiceGame 또는 Hamster 중 하나 이지만 thing은 이것들의 타입은 아님
  • TextRepresentable 타입이고 TextRepresentable은 textualDescription 프로퍼티를 가지고 있다는 것을 알고 있으므로 루프를 통해 매번 thing.textualDescription 에 안전하게 접근가능

 

프로토콜 상속

swift
닫기
protocol InheritingProtocol: SomeProtocol, AnotherProtocol { ​​​​// protocol definition goes here }
  • 프로토콜은 하나 또는 그 이상의 다른 프로토콜을 상속 할 수 있고 상속한 요구사항 위에 다른 요구사항 추가 가능
  • 프로토콜 상속에 대한 구문은 클래스 상속에 대한 구문과 유사하지만 콤마로 구분하여 여러개의 상속된 프로토콜을 목록화

 

swift
닫기
protocol PrettyTextRepresentable: TextRepresentable { ​​​​var prettyTextualDescription: String { get } }

 

  • PrettyTextRepresentable 을 채택하는 모든 것은 TextRepresentable 과 PrettyTextRepresentable 의 모든 요구사항을 충족해야 함
  • PrettyTextRepresentable 은 String 을 반환하는 prettyTextualDescription 이라는 gettable 프로퍼티를 제공하기 위해 하나의 요구사항을 추가

 

swift
닫기
extension SnakesAndLadders: PrettyTextRepresentable { ​​​​var prettyTextualDescription: String { ​​​​​​​​var output = textualDescription + ":\n" ​​​​​​​​for index in 1...finalSquare { ​​​​​​​​​​​​switch board[index] { ​​​​​​​​​​​​case let ladder where ladder > 0: ​​​​​​​​​​​​​​​​output += "▲ " ​​​​​​​​​​​​case let snake where snake < 0: ​​​​​​​​​​​​​​​​output += "▼ " ​​​​​​​​​​​​default: ​​​​​​​​​​​​​​​​output += "○ " ​​​​​​​​​​​​} ​​​​​​​​} ​​​​​​​​return output ​​​​} } print(game.prettyTextualDescription) // A game of Snakes and Ladders with 25 squares: // ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○

 

  • PrettyTextRepresentable 프로토콜을 채택하고 SnakesAndLadders 타입에 대해 prettyTextualDescription 프로퍼티의 구현을 제공
  • PrettyTextRepresentable 인 모든 것은 TextRepresentable 이어야 함 (상속 받았으므로)
  • prettyTextualDescription 의 구현은 출력 문자열을 시작하기 위해 TextRepresentable 프로토콜에서 textualDescription 프로퍼티를 접근하는 것으로 시작
  • prettyTextualDescription 프로퍼티는 이제 모든 SnakesAndLadders 인스턴스의 텍스트 설명을 출력하기 위해 사용될 수 있음

 

클래스 전용 프로토콜

 

swift
닫기
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol { ​​​​// class-only protocol definition goes here }
  • 프로토콜의 채택 목록에 AnyObject 프로토콜을 추가하여 구조체 또는 열거형이 아닌 클래스 타입으로 제한 가능
  • SomeClassOnlyProtocol 을 구조체 또는 열거형 정의에 채택하면 컴파일 시 에러가 발생
  • 프로토콜의 요구사항에 의해 정의된 동작이 준수하는 타입에 값 의미 체계가 아닌 참조 의미 체계가 있다고 가정하거나 요구하는 경우 클래스 전용 프로토콜을 사용
 

 

프로토콜 구성

  • 프로토콜 구성 (protocol composition) 을 사용하여 여러 프로토콜을 단일 요구사항으로 결합할 수 있음
  • 프로토콜 구성은 구성에 모든 프로토콜의 결합된 요구사항을 가진 임시 로컬 프로토콜로 정의된 것처럼 동작
  • 프로토콜 구성은 새로운 프로토콜 타입을 정의하지 않음
  • 프로토콜 구성은 SomeProtocol & AnotherProtocol 형식
  • 앰퍼샌드 (&)로 구분하여 많은 프로토콜을 목록화 가능
  • 프로토콜 목록 외에도 프로토콜 구성은 요구된 상위 클래스를 지정하는데 사용할 수 있는 하나의 클래스 타입을 포함할 수도 있음

 

swift
닫기
protocol Named { ​​​​var name: String { get } } protocol Aged { ​​​​var age: Int { get } } struct Person: Named, Aged { ​​​​var name: String ​​​​var age: Int } func wishHappyBirthday(to celebrator: Named & Aged) { ​​​​print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!") } let birthdayPerson = Person(name: "Malcolm", age: 21) wishHappyBirthday(to: birthdayPerson) // Prints "Happy birthday, Malcolm, you're 21!"
  • Named와 Aged, 두 프로토콜 모두 Person 이라는 구조체에 의해 채택
  • wishHappyBirthday(to:) 함수에서 celebrator 파라미터의 타입은 "Named 와 Aged 프로토콜 모두 준수하는 타입" 이라는 의미인 Named & Aged
  • 요구된 프로토콜 모두 준수하는 한 함수에 전달되는 특정 유형은 중요하지 않음
  • Person 은 프로토콜 모두 준수하기 때문에 이 호출은 유효하고 wishHappyBirthday(to:) 함수 동작

 

swift
닫기
class Location { ​​​​var latitude: Double ​​​​var longitude: Double ​​​​init(latitude: Double, longitude: Double) { ​​​​​​​​self.latitude = latitude ​​​​​​​​self.longitude = longitude ​​​​} } class City: Location, Named { ​​​​var name: String ​​​​init(name: String, latitude: Double, longitude: Double) { ​​​​​​​​self.name = name ​​​​​​​​super.init(latitude: latitude, longitude: longitude) ​​​​} } func beginConcert(in location: Location & Named) { ​​​​print("Hello, \(location.name)!") } let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3) beginConcert(in: seattle) // Prints "Hello, Seattle!"
  • beginConcert(in:) 함수는 "Location 의 하위 클래스와 Named 프로토콜을 준수하는 모든 타입" 이라는 뜻의 Location & Named 타입의 파라미터를 가짐
  • Person 은 Location 의 하위 클래스가 아니므로 beginConcert(in:) 함수로 birthdayPerson 전달은 유효하지 않음
  • 마찬가지로 Named 프로토콜을 준수하지 않고 Location 의 하위 클래스를 만들어 타입의 인스턴스로 beginConcert(in:) 을 호출해도 유효하지 않음

 

프로토콜 준수에 대한 검사

 

  • 프로토콜 준수성에 대해 확인하고 특정 프로토콜로 캐스팅 하기 위해 is 와 as 연산자를 사용가능
  • 프로토콜을 확인하고 캐스팅하는 것은 타입을 확인하고 캐스팅 하는 것과 정확하게 같은 구문을 따름
    • is 연산자는 인스턴스가 프로토콜을 준수한다면 true 를 반환하고 그렇지 않으면 false 를 반환
    • as? 는 프로토콜의 타입의 옵셔널 값을 반환하고 인스턴스가 프로토콜을 준수하지 않으면 그 값은 nil
    • as! 는 프로토콜 타입으로 강제로 다운 캐스팅 하고 다운 캐스트가 성공하지 못하면 런타임 에러 발생

 

swift
닫기
protocol HasArea { ​​​​var area: Double { get } } class Circle: HasArea { ​​​​let pi = 3.1415927 ​​​​var radius: Double ​​​​var area: Double { return pi * radius * radius } ​​​​init(radius: Double) { self.radius = radius } } class Country: HasArea { ​​​​var area: Double ​​​​init(area: Double) { self.area = area } }
  • Circle 클래스는 저장된 radius 프로퍼티 기반으로 계산된 프로퍼티로 area 프로퍼티 요구사항을 구현
  • Country 클래스는 저장된 프로퍼티로 직접 area 요구사항을 구현
  • 두 클래스 모두 HasArea 프로토콜을 준수

 

swift
닫기
class Animal { ​​​​var legs: Int ​​​​init(legs: Int) { self.legs = legs } } let objects: [AnyObject] = [ ​​​​Circle(radius: 2.0), ​​​​Country(area: 243_610), ​​​​Animal(legs: 4) ]
  • Circle, Country 그리고 Animal 클래스는 공유된 기본 클래스가 없음
  • 하지만 모두 클래스이므로 모든 세가지 타입의 인스턴스는 타입 AnyObject 의 값을 저장하는 배열을 초기화 하기위해 사용될 수 있음
  • objects 배열은 2의 반지름을 가진 Circle 인스턴스, 영국의 표면적으로 초기화 된 Country 인스턴스 그리고 4개의 다리를 가진 Animal 인스턴스를 포함하는 배열 리터럴로 초기화

 

swift
닫기
for object in objects { ​​​​if let objectWithArea = object as? HasArea { ​​​​​​​​print("Area is \(objectWithArea.area)") ​​​​} else { ​​​​​​​​print("Something that doesn't have an area") ​​​​} } // Area is 12.5663708 // Area is 243610.0 // Something that doesn't have an area
  • 반복문을 통해 objects 배열의 각 객체가 HasArea 프로토콜을 준수하는지 확인 가능
  • 배열의 객체가 HasArea 프로토콜을 준수할 때마다 as? 연산자에 의해 반환된 옵셔널 값은 objectWithArea 라는 상수에 옵셔널 바인딩으로 언래핑
  • objectWithArea 상수는 HasArea 타입으로 알고 있으므로 area 프로퍼티는 접근 가능하고 안전하게 출력 가능
  • 기본 객체는 캐스팅 프로세스에 의해 변경되지 않음
  • objectWithArea 상수에 저장될 때 모두 다른 클래스이지만 HasArea 타입으로 캐스팅했기 때문에 area 프로퍼티만 접근 가능
 

옵셔널 프로토콜 요구사항

 

  • 프로토콜에 대해 옵셔널 요구사항 (optional requirements) 을 정의할 수 있음
  • 이 요구사항은 프로토콜을 준수하는 타입으로 구현될 필요가 없음
  • 옵셔널 요구사항은 프로토콜의 정의의 부분으로 optional 수식어를 앞에 붙임
  • 옵셔널 요구사항은 Objective-C와 상호운용되는 코드를 작성할 수 있음
  • 프로토콜과 옵셔널 요구사항 모두 @objc 속성으로 표시되어야 함
  • @objc 프로토콜은 Objective-C 클래스나 다른 @objc 클래스로 부터 상속한 클래스에만 채택될 수 있음
  • 구조체나 열거형에 의해 채택될 수 없음
  • 옵셔널 요구사항에서 메서드나 프로퍼티를 사용할 때 타입은 자동으로 옵셔널, 예를 들어 (Int) -> String 타입의 메서드는 ((Int) -> String)?
  • 전체 함수 타입은 메서드의 반환값이 아니라 옵셔널로 래핑
  • 옵셔널 프로토콜 요구사항은 프로토콜을 준수하는 타입에 의해 요구사항이 구현되지 않았을 가능성을 나타내기 위해 옵셔널 체이닝으로 호출될 수 있음
  • 호출될 때 someOptionalMethod?(someArgument) 와 같이 메서드의 이름 뒤에 물음표를 작성하여 옵셔널 메서드의 구현에 대해 확인
 
swift
닫기
@objc protocol CounterDataSource { ​​​​@objc optional func increment(forCount count: Int) -> Int ​​​​@objc optional var fixedIncrement: Int { get } }
  • CounterDataSource 프로토콜은 increment(forCount:) 라는 옵셔널 메서드 요구사항과 fixedIncrement 라는 옵셔널 프로퍼티 요구사항을 정의
  • 이 요구사항은 Counter 인스턴스에 대해 적절한 증가값을 제공하기 위해 데이터 소스에 대한 2가지 다른 방법을 정의
  • 엄밀히 말하면 프로토콜 요구사항을 구현하지 않고도 CounterDataSource 를 준수하는 사용자 정의 클래스를 작성 가능
  • 둘다 옵셔널이라 기술적으로는 허용되지만 좋은 데이터 소스로는 적합합지 않음

 

swift
닫기
class Counter { ​​​​var count = 0 ​​​​var dataSource: CounterDataSource? ​​​​func increment() { ​​​​​​​​if let amount = dataSource?.increment?(forCount: count) { ​​​​​​​​​​​​count += amount ​​​​​​​​} else if let amount = dataSource?.fixedIncrement { ​​​​​​​​​​​​count += amount ​​​​​​​​} ​​​​} }
  • Counter 클래스는 count 라는 프로퍼티 변수에 현재값을 저장
  • Counter 클래스는 메서드가 호출될 때마다 count 프로퍼티를 증가하는 increment 라는 메서드도 정의
  • increment() 메서드는 먼저 데이터 소스에 increment(forCount:) 메서드의 구현을 통해 증가값을 조회
  • increment() 메서드는 increment(forCount:) 호출에 대해 옵셔널 체이닝을 사용하고 메서드의 단일 인자로 현재 count 값을 전달
  • dataSource 가 nil 이거나 데이터 소스가 increment(forCount:) 를 구현하지 않아 increment(forCount:) 로 부터 값을 조회할 수 없는 경우, increment() 메서드는 데이터 소스의 fixedIncrement 프로퍼티를 대신 조회 시도
  • fixedIncrement 프로퍼티도 옵셔널 요구사항이므로 그 값은 fixedIncrement 가 CounterDataSource 프로토콜 정의의 부분으로 옵셔널이 아닌 Int 프로퍼티로 정의되었어도 옵셔널 Int 값

 

swift
닫기
class ThreeSource: NSObject, CounterDataSource { ​​​​let fixedIncrement = 3 } var counter = Counter() counter.dataSource = ThreeSource() for _ in 1...4 { ​​​​counter.increment() ​​​​print(counter.count) } // 3 // 6 // 9 // 12
  • 새로운 Counter 인스턴스를 생성하고 데이터 소스를 새로운 ThreeSouce 인스턴스로 설정
  • 카운터의 increment() 메서드를 4번 호출
  • 예상대로 카운터의 count 프로퍼티는 increment() 가 호출될 때마다 3씩 증가

 

swift
닫기
class TowardsZeroSource: NSObject, CounterDataSource { ​​​​func increment(forCount count: Int) -> Int { ​​​​​​​​if count == 0 { ​​​​​​​​​​​​return 0 ​​​​​​​​} else if count < 0 { ​​​​​​​​​​​​return 1 ​​​​​​​​} else { ​​​​​​​​​​​​return -1 ​​​​​​​​} ​​​​} } counter.count = -4 counter.dataSource = TowardsZeroSource() for _ in 1...5 { ​​​​counter.increment() ​​​​print(counter.count) } // -3 // -2 // -1 // 0 // 0
  • TowardsZeroSource 클래스는 CounterDataSource 프로토콜로 부터 옵셔널 increment(forCount:) 메서드를 구현
  • 카운트 방향을 정하기 위해 count 인자값을 사용
  • count 가 0이면 이 메서드는 더이상 카운트 작업을 진행하지 않음을 나타내기 위해 0 을 반환
  • -4 부터 0까지 카운트 하기 위해 존재하는 Counter 인스턴스와 함께 TowardsZeroSource 의 인스턴스를 사용 가능

 

프로토콜 확장

 

  • 프로토콜은 채택된 타입에 제공하기 위한 메서드, 초기화 구문, 서브 스크립트, 그리고 계산된 프로퍼티 구현을 확장 가능
  • 이를 통해 각 타입의 개별 적합성 또는 전역 함수가 아닌 프로토콜 자체에 동작을 정의 가능

 

swift
닫기
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"
  • RandomNumberGenerator 프로토콜은 임의의 Bool 값을 반환하기 위해 필요한 random() 메서드의 결과를 사용하는 randomBool() 메서드를 제공하기 위해 확장
  • 프로토콜 확장을 생성함으로써 모든 준수하는 타입은 추가 수정없이 메서드 구현을 자동으로 얻음
  • 프로토콜 확장은 준수하는 타입에 구현을 추가할 수 있지만 프로토콜을 확장하거나 다른 프로토콜을 상속할 수 없음
  • 프로토콜 상속은 항상 프로토콜 선언 자체에 지정

 

기본 구현 제공

  • 해당 프로토콜의 모든 메서드 또는 계산된 프로퍼티 요구사항에 기본 구현을 제공하기 위해 프로토콜 확장을 사용할 수 있음
  • 준수하는 타입이 필수 메서드 또는 프로퍼티의 자체 구현을 제공하면 해당 구현은 확장에 의해 제공되는 구현 대신 사용됨
  • 확장에 의해 제공된 기본 구현을 가진 프로토콜 요구사항은 옵셔널 프로토콜 요구사항과 다름
  • 준수하는 타입이 자체 구현을 제공할 필요는 없지만 기본 구현을 가진 요구사항은 옵셔널 체이닝 없이 호출될 수 있음

 

swift
닫기
extension PrettyTextRepresentable { ​​​​var prettyTextualDescription: String { ​​​​​​​​return textualDescription ​​​​} }
  • TextRepresentable 프로토콜을 상속하는 PrettyTextRepresentable 프로토콜은 textualDescription 프로퍼티 접근의 결과를 반환하기 위해 필요한 prettyTextualDescription 프로퍼티의 기본 구현을 제공할 수 있음

 

프로토콜 확장에 제약사항 추가

  • 프로토콜 확장을 정의할 때 확장의 메서드와 프로퍼티를 사용할 수 있기 전에 준수하는 타입이 충족해야 하는 제약조건을 지정 가능
  • 일반적인 where 절을 작성하여 확장하는 프로토콜의 이름 뒤에 제약조건을 작성

 

swift
닫기
extension Collection where Element: Equatable { ​​​​func allEqual() -> Bool { ​​​​​​​​for element in self { ​​​​​​​​​​​​if element != self.first { ​​​​​​​​​​​​​​​​return false ​​​​​​​​​​​​} ​​​​​​​​} ​​​​​​​​return true ​​​​} }
  • Equatable 프로토콜을 준수하는 항목의 모든 콜렉션에 적용하는 Collection 프로토콜의 확장을 정의
  • 콜렉션의 요소를 표준 라이브러리의 일부인 Equatable 프로토콜로 제한하면 두 요소간의 같음과 다름에 대한 확인을 위해 == 와 != 연산자를 사용 가능
  • allEqual() 메서드는 콜렉션에 모든 요소가 같을 때만 true 를 반환
  • 모든 요소가 같고 하나만 다른 정수의 2개의 배열의 경우, 배열은 Collection 을 준수하고 정수는 Equatable 을 준수하므로 equalNumbers 와 differentNumbers 는 allEqual() 메서드를 사용할 수 있음
  • 준수하는 타입이 같은 메서드 또는 프로퍼티에 대한 구현을 제공하는 여러 제약조건의 확장에 대한 요구사항을 충족한다면 Swift는 가장 전문화 된 제약조건에 해당하는 구현을 사용

 

자료 출처

https://docs.swift.org/swift-book/LanguageGuide/Protocols.html

 

'iOS' 카테고리의 다른 글

Swift - Extensions  (0) 2023.03.05
Swift - Concurrency (동시성)  (0) 2023.02.26
Swift - Protocol(메서드, 초기화) (2/4)  (0) 2023.02.06
Swift - Protocol(정의, 프로퍼티) (1/4)  (0) 2023.02.03
Table View의 Self-Sizing Cell  (0) 2022.08.10