LoginSignup
0
3

More than 1 year has passed since last update.

【Swift】標準で定義されているプロトコルについて

Posted at

はじめに

標準で定義されているプロトコルについて簡単にまとめてみました

内容

Equatableプロトコル

Equatableに準拠することで、値が等しいかどうかを比較できる

protocol Equatable

Equatable に準拠するするには、
構造体の場合、格納されているすべてのプロパティが Equatable に適合していること(enum の場合、関連するすべての値が Equatable に適合していること)。enum の場合、関連する値はすべて Equatable に準拠する必要がある(関連する値がない enum は、宣言がなくても Equatable 準拠です)

class StreetAddress {
    let number: String
    let street: String
    let unit: String?

    init(_ number: String, _ street: String, unit: String? = nil) {
        self.number = number
        self.street = street
        self.unit = unit
    }
}

👇

extension StreetAddress: Equatable {
    static func == (lhs: StreetAddress, rhs: StreetAddress) -> Bool {
        return
            lhs.number == rhs.number &&
            lhs.street == rhs.street &&
            lhs.unit == rhs.unit
    }
}

Comparableプロトコル

Comparable に準拠することで、値の大小関係を比較することができる

protocol Comparable : Equatable

Comparable に準拠するするには、型の静的メソッドとして<と==演算子を定義

struct Date {
    let year: Int
    let month: Int
    let day: Int
}

👇

extension Date: Comparable {
    static func < (lhs: Date, rhs: Date) -> Bool {
        if lhs.year != rhs.year {
            return lhs.year < rhs.year
        } else if lhs.month != rhs.month {
            return lhs.month < rhs.month
        } else {
            return lhs.day < rhs.day
        }
    }
    static func == (lhs: Date, rhs: Date) -> Bool {
        return lhs.year == rhs.year && lhs.month == rhs.month
            && lhs.day == rhs.day
    }
}

Identifiableプロトコル

Identifiable に準拠することで、クラスまたは値の型にIDの概念を提供することができる

protocol Identifiable

IDは以下のいずれかの特性を持つ

  • UUIDのように常に一意であることが保証される
  • データベースのレコードキーのように、環境ごとに永続的に一意である
  • グローバルインクリメント整数のように、プロセスの有効期間中一意である
  • オブジェクト識別子のように、オブジェクトの寿命に対して一意である
  • コレクションインデックスのように、現在のコレクション内で一意である

Identifiable に準拠するするには、var idを定義

associatedtype ID : Hashable

var id: Self.ID

Hashableプロトコル

Hashableプロトコルに準拠した型は、その値をもとにハッシュ値(もとの値から特定のアルゴリズムで算出されるInt型の値)を計算できる

protocol Hashable : Equatable

独自のカスタム型をセットまたは辞書のキー型として使用するには、その型に Hashable 準拠を追加する

Hashable に準拠するには、hash(into:)メソッドを実装する
hash(into:)の実装では、提供された Hasher のインスタンスに対して、 combine(_:) を呼び出す

struct GridPoint {
    var x: Int
    var y: Int
}

👇

extension GridPoint: Hashable {
    static func == (lhs: GridPoint, rhs: GridPoint) -> Bool {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(x)
        hasher.combine(y)
    }
}

CustomStringConvertibleプロトコル

CustomStringConvertibleプロトコルに準拠した型は、インスタンスを文字列に変換する際、独自の表現を提供ができる

protocol CustomStringConvertible

Hashable に準拠するするには、var description: Stringを定義

struct Point {
    let x: Int, y: Int
}

let p = Point(x: 21, y: 30)
print(p)
// Prints "Point(x: 21, y: 30)"

👇

extension Point: CustomStringConvertible {
    var description: String {
        return "(\(x), \(y))"
    }
}

print(p)
// Prints "(21, 30)"

CaseIterableプロトコル

CaseIterable プロトコルに準拠する型は、通常、associated valueを持たない列挙型
CaseIterable 型を使用する場合、型の allCases プロパティを使用すると、その型のすべてのケースのコレクションにアクセスすることができる

protocol CaseIterable

associated valueや @available 属性がない列挙型の場合、CaseIterable 要件の実装を自動的に提供することができる

enum CompassDirection: CaseIterable {
    case north, south, east, west
}

print("There are \(CompassDirection.allCases.count) directions.")
// Prints "There are 4 directions."
let caseList = CompassDirection.allCases
                               .map({ "\($0)" })
                               .joined(separator: ", ")
// caseList == "north, south, east, west"

RawRepresentableプロトコル

RawRepresentableプロトコルに準拠した型は、元のタイプの値を失うことなく、カスタムタイプと関連するタイプを切り替えることができる
RawRepresentableプロトコルは主に、rawValueを持つ列挙型とオプションセットに関連している

protocol RawRepresentable
rawValueを持つ列挙型

文字列、整数などの列挙型は、自動的にRawRepresentableとなる

enum Counter: Int {
    case one = 1, two, three, four, five
}

for i in 3...6 {
    print(Counter(rawValue: i))
}
// Prints "Optional(Counter.three)"
// Prints "Optional(Counter.four)"
// Prints "Optional(Counter.five)"
// Prints "nil"

オプションセット

オプションセットは、OptionSetプロトコルを使った継承により、全てRawRepresentableに準拠しています。オプションセットを使うにせよ、自分で作るにせよ、オプションセットのインスタンスのrawValueを使って、そのインスタンスのビットフィールドを格納します。したがって、rawValueは UInt8 や Int など、FixedWidthInteger プロトコルに準拠した型である必要があります。たとえば、Direction 型は、ゲーム内で移動できる 4 方向のオプションセットを定義する

struct Directions: OptionSet {
    let rawValue: UInt8

    static let up    = Directions(rawValue: 1 << 0)
    static let down  = Directions(rawValue: 1 << 1)
    static let left  = Directions(rawValue: 1 << 2)
    static let right = Directions(rawValue: 1 << 3)
}

let allowedMoves: Directions = [.up, .down, .left]
print(allowedMoves.rawValue)
// Prints "7"

オプションセットについては、こちらの記事が分かりやすかったです

おわりに

基本的な部分を一部のみまとめたものですが、一度整理できてスッキリしました。

参考

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3