Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
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

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

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

 

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 프로토콜을 채택하고 준수하기 위해 확장가능

 

조건적으로 프로토콜 준수

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 절을 작성하여 채택 중인 프로토콜의 이름 뒤에 제약조건을 작성

 

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

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 프로토콜을 준수하는 연관된 타입만 있는 열거형
    • 연관된 타입이 없는 열거형

 

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 는 등가 연산자의 합성된 구현을 받음

 

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 프로토콜의 기본 구현은 나머지 비교 연산자를 제공

 

프로토콜 타입의 콜렉션

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

 

 

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 에 안전하게 접근가능

 

프로토콜 상속

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

 

protocol PrettyTextRepresentable: TextRepresentable {
    var prettyTextualDescription: String { get }
}

 

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

 

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 인스턴스의 텍스트 설명을 출력하기 위해 사용될 수 있음

 

클래스 전용 프로토콜

 

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

 

프로토콜 구성

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

 

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:) 함수 동작

 

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! 는 프로토콜 타입으로 강제로 다운 캐스팅 하고 다운 캐스트가 성공하지 못하면 런타임 에러 발생

 

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 프로토콜을 준수

 

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 인스턴스를 포함하는 배열 리터럴로 초기화

 

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) 와 같이 메서드의 이름 뒤에 물음표를 작성하여 옵셔널 메서드의 구현에 대해 확인
 
@objc protocol CounterDataSource {
    @objc optional func increment(forCount count: Int) -> Int
    @objc optional var fixedIncrement: Int { get }
}
  • CounterDataSource 프로토콜은 increment(forCount:) 라는 옵셔널 메서드 요구사항과 fixedIncrement 라는 옵셔널 프로퍼티 요구사항을 정의
  • 이 요구사항은 Counter 인스턴스에 대해 적절한 증가값을 제공하기 위해 데이터 소스에 대한 2가지 다른 방법을 정의
  • 엄밀히 말하면 프로토콜 요구사항을 구현하지 않고도 CounterDataSource 를 준수하는 사용자 정의 클래스를 작성 가능
  • 둘다 옵셔널이라 기술적으로는 허용되지만 좋은 데이터 소스로는 적합합지 않음

 

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 값

 

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씩 증가

 

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 의 인스턴스를 사용 가능

 

프로토콜 확장

 

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

 

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() 메서드를 제공하기 위해 확장
  • 프로토콜 확장을 생성함으로써 모든 준수하는 타입은 추가 수정없이 메서드 구현을 자동으로 얻음
  • 프로토콜 확장은 준수하는 타입에 구현을 추가할 수 있지만 프로토콜을 확장하거나 다른 프로토콜을 상속할 수 없음
  • 프로토콜 상속은 항상 프로토콜 선언 자체에 지정

 

기본 구현 제공

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

 

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

 

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

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

 

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
Comments