はじめに
標準で定義されているプロトコルについて簡単にまとめてみました
内容
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"
オプションセットについては、こちらの記事が分かりやすかったです
おわりに
基本的な部分を一部のみまとめたものですが、一度整理できてスッキリしました。
参考