Sponsor Link
環境&対象
- macOS14.5
- Xcode 15.4
- iOS 17.5
- Swift 5.9
Foundation Protocol シリーズ
[Swift] Standard Library の Equatable を理解する [Swift] Standard Library の Comparable を理解する
今後の予定: Hashable, Identifiable
Protocol Equatable
Equatable というのは、Swift の Standard Library が用意している Protocol の1つです。
参考
EquatableApple Developer Documentation
Protocol Equatable とは
Equatable に conform するということは、その名の通り、同値判定できる型であることを表明しているということです。
Equate (同一視する) + able (可能にする) = Equatable です。
Swift の Standard Library では、Collection でのメソッドによく使用されています。
例えば、ある集合があって、その集合に特定の要素がふくまれているかどうか をチェックするということは、その集合にふくまれているいずれかの要素が 特定の要素と同値であるかどうか をチェックするということになります。
Collection の1つである Array には、以下のようなメソッドが用意されていて、ある要素が その Array に含まれているかどうかを判定できます。条件は、Element が Equatable に conform していることです。
func contains(_ element: Self.Element) -> Bool
Available when Element conforms to Equatable.
Equatable を使用して、同値判定をしていると思われます。そのままですね。
Protocol が要求するメソッド
次に、Equatable という Protocol に conform させるとは、どういうことかを確認します。
Equatable に conform させるとは、以下のメソッドを定義することです。
static func == (lhs: Self, rhs: Self) -> Bool
Equatable は、struct/ enum/ class/ actor に対して適用することができます。
多くのケースでは同値判定する時に、それぞれのプロパティ同士の比較を用いますが、上記の static メソッドは、同期的(sync) であることが必要です。
言い換えると、actor isolated されているプロパティを比較に使用するために、上記を 非同期(async) なメソッドとして定義しても、Equatable に conform したことにはなりません。
Protocol が要求するのは、同期的(sync) な static メソッドです。
指定メソッドを定義することで、conform させることができるので、extension で定義することでも conform させることもできます。
すでに Equatable 準拠している型とその組み合わせ
Swift の基本的な型の大半は、Equatable に conform しています。
ですので、Double, Int, String 等もすでに Equatable です。具体的な型が Equatable に conform しているかは、ドキュメントを確認してください。
Equatable な stored property/associated value を持つ struct/enum の扱い
Equatable に conform している型を stored property として持っている struct や associated value として持っている enum については、Equatable に conform していることを宣言すると、追加実装なしに Equatable に conform することができます。
なお、その際には、すべての stored property や associated value が同値であることが、struct/ enum が同値と判定される条件となります。
Equatable が満たすべき条件
Equatable で同値と判定される条件として、大抵は、同じ値であることがその条件になるかと思いますが、Swift 言語としてそのような制約があるわけではありません。
Equatable での判定が決定的でなくなってしまうことが考えられるため、Equatable に conform するための判定としては、以下を満たすことが必要です。
・a == a は、常に成り立つこと (反射律)
・a == b であるとき、b == a も成り立つこと (対象律)
・a == b でかつ b == c であるとき、a == c も成り立つこと (推移律)
デフォルトの実装を使用している時は問題ありませんが、自分で 少しトリッキーな == を実装するときには気をつけないといけません。
独自実装してみる
以下のような struct を独自実装で、Equatable に conform させてみます。
struct Person {
var familyName: String?
var givenName: String?
}
よくある(?) 氏名を持つ struct ですが、不明な場合に省略できるように Optional にしています。
省略されている部分は同値でないものとして 同値判定する == を定義してみます。
extension Person: Equatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
Self.sameName(lhs: lhs.familyName, rhs: rhs.familyName) && Self.sameName(lhs: lhs.givenName, rhs: rhs.givenName)
}
static func sameName(lhs: String?, rhs: String?) -> Bool {
if let lhsName = lhs,
let rhsName = rhs {
return lhsName == rhsName
}
return false
}
}
ちょっと適当な同値関係ですが、先ほどの 満たすべき条件は、ちゃんと 満たされています。
以下のように実行してみることで、期待通り(?)に動作していることも確認できます。
import Foundation
struct Person {
var familyName: String?
var givenName: String?
}
extension Person: Equatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
Self.sameName(lhs: lhs.familyName, rhs: rhs.familyName) && Self.sameName(lhs: lhs.givenName, rhs: rhs.givenName)
}
static func sameName(lhs: String?, rhs: String?) -> Bool {
if let lhsName = lhs,
let rhsName = rhs {
return lhsName == rhsName
}
return false
}
}
let suzukiIchiro = Person(familyName: "Suzuki", givenName: "Ichiro")
let tsunodaYuki = Person(familyName: "Tsunoda", givenName: "Yuki")
let ichiroKun = Person(familyName: nil, givenName: "Ichiro")
let suzukiSan = Person(familyName: "Suzuki", givenName: nil)
let johnDoe = Person(familyName: nil, givenName: nil)
let anotherSuzukiIchiro = Person(familyName: "Suzuki", givenName: "Ichiro")
print(suzukiIchiro == suzukiSan) // false
print(suzukiIchiro == ichiroKun) // false
print(suzukiIchiro == tsunodaYuki) // false
print(tsunodaYuki == suzukiSan) // false
print(suzukiIchiro == johnDoe) // false
print(tsunodaYuki == johnDoe) // false
print(suzukiIchiro == anotherSuzukiIchiro) // true
NSObject の Equatable
NSObject は、Equatable に準拠しています。同値判定の基準は 「同じインスタンスかどうか」 です。
内部的には、isEqual(to: Any?) を使用していますので、挙動を変更したい場合は override することが必要です。
まとめ
Standard Library の Equatable
- 同値関係を確認できることを表明している Protocol
- Swift で提供される型の大半は conform している
- 自分で conform させるときは、static func ==(lhs:Self, rhs:Self) を定義する
- 同値関係は、反射律/ 対象律/ 推移律 が成立しないといけない
- struct の stored property/ enum の associated value が Equatable であれば、struct/ enum を Equatable に conform させるだけで実装なしに conform できる
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
SwiftUI おすすめ本
SwiftUI を理解するには、以下の本がおすすめです。
SwiftUI ViewMatery
SwiftUI で開発していくときに、ViewやLayoutのための適切なmodifierを探すのが大変です。
英語での説明になってしまいますが、以下の”SwiftUI Views Mastery Bundle”という本がビジュアル的に確認して探せるので、便利です。
英語ではありますが、1ページに コードと画面が並んでいるので、非常にわかりやすいです。
View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。
超便利です
販売元のページは、こちらです。
SwiftUI 徹底入門
# SwiftUI は、毎年大きく改善されていますので、少し古くなってしまいましたが、いまでも 定番本です。
Swift学習におすすめの本
詳解Swift
Swift の学習には、詳解 Swift という書籍が、おすすめです。
著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。
最新版を購入するのがおすすめです。
現時点では、上記の Swift 5 に対応した第5版が最新版です。
Sponsor Link