在咱們進行 App 開發的時候,常常會用到的一個操做就是判斷兩個對象是否相等。好比兩個字符串是否相等。而所謂的 相等 有着兩層含義。一個是值相等,還有一個是引用相等。若是熟悉 Objective-C 開發的話,就會知道 Objective-C 爲咱們提供了一系列 isEqual:
方法來判斷值相等,而 == 等於號用來判斷引用相等。 咱們來看一個 Objective-C 的例子就會更加明白了:php
NSArray *arr1 = @[@"cat",@"hat",@"app"];
NSArray *arr2 = @[@"cat",@"hat",@"app"];
NSLog(@"result %i", (arr1 == arr2)); // result 0
NSLog(@"result %i", [arr1 isEqualToArray:arr2]); // result 1
上面的代碼中,咱們定義了兩個數組,arr1 和 arr2 這兩個數組中保存的是一樣的元素。接下來咱們對他們進行相等比較並輸出結果。第一次咱們用 == 等於號進行比較,返回的結果是 0, 也就是說比較失敗了。緣由相信有些經驗的同窗都應該明白,是由於 在 Objective-C 中 == 比較的是引用,由於 arr1 和 arr2 是兩個不一樣的對象,因此即使他們的值相同,但他們的引用也不相同。因此 == 比較會失敗。而第二個比較用得是 NSArray 的 isEqualToArray 方法,這個方法是用來進行值比較的,由於這兩個數組的值相同,因此 isEqualToArray 的比較方法會返回 1。css
<!-- more -->html
咱們經過 Objective-C 瞭解了相等操做的運做機制,包含了值相等和引用相等。如今咱們回到 Swift 中來。相信有 Objective-C 經驗的同窗必定會在找 isEqual 方法,但會發現 Swift 的原生類中沒有 isEqual 的定義。java
若是想對 isEqual 進行更深刻的瞭解,這篇文章 Equality 裏面有詳細的講解。咱們這裏不過多敘述。nginx
Swift 中沒有提供 iEqual 方法的定義,而是用另外一種更加天然的方式,重載操做符 來處理相等判斷的。重載 ==
操做符是經過 Equatable 協議來完成的。咱們經過實現這個協議中的方法來實現操做符的重載。web
func ==(lhs: Self, rhs: Self) -> Bool
Swift 中本身實現了數組類,叫作 Array
, 關於 Array
類型,這篇文章有詳細的敘述 Swift Tips - Array 類型。Swift 中的 Array
類型實現了 Equatable
協議,因此咱們能夠對 Array
類型的實例經過 ==
操做符進行比較:canvas
var arr1 = ["cat","hat","app"]
var arr2 = ["cat","hat","app"]
println arr1 == arr2 // true
咱們看到,在 Swift 中直接使用 ==
進行比較的效果和 Objective-C 中的 isEqual:
方法的實際效果是同樣的。他們都對值進行比較。Swift 的這點特性和 Objective-C 略有不一樣。Swift 的原生類中,幾乎都實現了 Equatable
協議。swift
咱們如今瞭解了 Swift 中的 Equatable
協議。相信細心的同窗已經發現了,咱們在 Swift 中之因此能夠對實例用 ==
進行比較,是由於這個符號操做的對象實現了 Equatable
協議。那若是對沒實現這個協議的對象使用 ==
進行比較呢?咱們能夠看一下這段代碼:數組
上面的代碼中,咱們定義了一個 Name
類,並實例化了這個類的兩個對象 john
和 copy
。而後咱們用 ==
對這兩個進行比較,這時編譯器報錯了。緣由就是咱們這個類沒有實現 Equatable
協議。微信
那麼下面咱們對這個類進行一下修改:
class Name :Equatable {
var firstName:String?
var lastName:String?
init(firstName first:String, lastName last:String){
self.firstName = first
self.lastName = last
}
}
func ==(lhs: Name, rhs: Name) -> Bool {
return lhs.firstName == rhs.firstName && lhs.lastName == rhs.lastName
}
在咱們的修改中,咱們實現了 Equatable
協議,並實現了 func ==(lhs: Name, rhs: Name) -> Bool
方法。判斷只有在 firstName
和 lastName
都相等的狀況下,這兩個 Name 對象纔算相等。
進行完這個修改後,咱們就能夠繼續使用咱們以前的判斷了:
if john == copy {
print("equal") //equal
}
此次,if
語句中的 print
方法被成功的執行了。
如今咱們知道了 Swift 中的大部分類都有本身對 ==
操做符的實現。那麼若是咱們如今想比較兩個對象的引用是否相等該怎麼辦呢?
咱們可使用 ObjectIdentifier
類來取得對象的引用標識,咱們明確的調用這個類的構造方法來取得對象的引用,並進行比較:
if ObjectIdentifier(john) == ObjectIdentifier(copy) {
print("equal")
}else{
print("not equal") // not equal
}
咱們用 ObjectIdentifier
取得了對象的引用地址,而且 ObjectIdentifier
自己又實現了 Equatable
協議,因此 咱們對轉換後的 ObjectIdentifier
對象用 ==
進行比較,就能夠判斷應用是否相同了。
咱們剛纔介紹了 Equatable
協議,和它相關的還有一個 Comparable
協議。 Equatable
協議是對相等性進行比較。而 Comparable
是對順序進行比較。好比 Swift 中的 Double
類,就實現了 Comparable
協議:
var left:Double = 30.23
var right:Double = 50.55
if left < right {
print("30.23 is less than 50.55")
}
實現了 Comparable
協議的類,就可使用 >,<,<=,>=
這些操做符進行比較了。這個協議的定義以下:
protocol Comparable { ... }
func <=(lhs: Self, rhs: Self) -> Bool func >=(lhs: Self, rhs: Self) -> Bool func >(lhs: Self, rhs: Self) -> Bool
你是否是會以爲,要實現比較操做,咱們要實現這裏面全部的方法呢。咱們不妨花幾分鐘時間仔細的看一看這個協議的定義,而且思考一下。
<=
,>=
,>
,< 你們能夠看一下這個協議,好像是否是少了 < 的定義呢?>=
, 其實是 >
和 ==
的一個並列關係,若是咱們實現了這兩個操做符,實際上 >=
的邏輯也就明確了。這個 >=
的操做符的邏輯通常就是這個語句: return lhs > rhs || lhs == rhs
。經過上面簡短的分析,咱們是否是發現了一個規律? 其實就是,咱們只要實現某幾個操做符,就能推出其餘操做符的邏輯。舉個例子,以上面的 Comparable
接口的定義來看,<=
和 >
這兩個操做符,咱們只須要實現其中一個就能夠推導出另一個。好比,咱們實現了 >
操做符,那麼 '<=' 操做符只須要的前面那個函數取反便可。
那麼,咱們 <=
函數的實現,只須要相似這樣實現便可:
func <=(lhs: Self, rhs: Self) -> Bool {
return !(lhs > rhs)
}
仔細想想,其實咱們只要實現 ==
和 <
操做符,其餘的幾個操做符都可以經過這兩個推導出來了:
<
的邏輯取反和 ==
一塊兒的邏輯或操做推導出。<
的邏輯取反推導出。<
和 ==
的邏輯或操做推導出。關於這方面的知識,有一個概念叫作 嚴格全序,有興趣的同窗能夠讀一讀。
那麼如今問題又來了,咱們其實只要實現 ==
和 <
方法的邏輯就能夠完成比較操做了,那麼對其餘操做符的實現代碼,其實都是同樣的。就顯得有些冗餘了。
而 Swift 正爲咱們解決了這個問題,爲咱們提供了協議的默認實現,仔細查看 Swift 文檔的話,咱們會發現除了 Comparable
協議,還定義了一個 _Comparable
協議,而前者繼承自後者。 _Comparable
協議的定義以下:
protocol _Comparable { ... }
func <(lhs: Self, rhs: Self) -> Bool
咱們發現,剛剛咱們丟失的 <
在這裏找到了。 _Comparable
就是 Swift 中提供的默認協議實現機制的例子。_Comparable
協議中提供了對其餘幾個操做符 >=,>,<=
的默認實現。
因爲 Comparable
同時繼承自 _Comparable
和 Equatable
協議,因此咱們只須要實現 <
和 ==
這兩個操做符,便可完成整個比較操做的實現。下面咱們來看一個例子:
class Name: Comparable {
var firstName:String
var lastName:String
init(firstName first:String,lastName last:String){
self.firstName = first
self.lastName = last
}
}
func ==(lhs: Name, rhs: Name) -> Bool {
return lhs.firstName == rhs.firstName && lhs.lastName == rhs.lastName
}
func <(lhs: Name, rhs: Name) -> Bool {
return lhs.firstName < rhs.firstName && lhs.lastName < rhs.lastName
}
let john = Name(firstName: "John", lastName: "Smith")
let johnClone = Name(firstName: "John", lastName: "Smith")
let jack = Name(firstName: "Jack", lastName: "Smith")
let mary = Name(firstName: "Mary", lastName: "Williams")
print(john >= jack) //true
print(john <= jack) //true
print(john == jack) //false
print(john > jack) //false
print(john < jack) //false
print(jack < mary) //true
print(john == johnClone) //true
var names:Array<Name> = [johnClone,mary,john,jack]
//[{firstName "John" lastName "Smith"}, {firstName "Mary" lastName "Williams"}, {firstName "John" lastName "Smith"}, {firstName "Jack" lastName "Smith"}]
names.sort { (lhs, rhs) -> Bool in
return lhs > rhs
}
//[{firstName "Mary" lastName "Williams"}, {firstName "John" lastName "Smith"}, {firstName "John" lastName "Smith"}, {firstName "Jack" lastName "Smith"}]
咱們在這裏完整的實現了 Comparable
協議,咱們定義了一個 Name
類,並實現了 <
和 ==
協議方法,其中一個來自 _Comparable
協議,一個來自 Equatable
協議。而 Comparable
協議中的三個比較方法,已經在 _Comparable
中提供了默認的實現,因此咱們就不用再本身實現一遍了。(注意兩個 Comparable 和 _Comparable 的下劃線區別,這是兩個不一樣的協議。)
最後一個要提到的還有 Hashable 協議。咱們對 Dictionary 的概念應該不陌生,那麼若是一個對象想做爲 Dictionary 的 key 的話,那麼就須要實現 Hashable 協議。
Swift 中如下這些類默認實現了 Hashable 協議:
因此這些類的實例,能夠做爲 Dictionary 的 key。 咱們來看一下 Hashable 協議的定義:
protocol Hashable { ... }
var hashValue: Int { get }
定義很是簡單,咱們只須要實現 hashValue
定義 hash 值的計算方法便可。關於 hash 值的優化方法咱們就又是另一個課題了,因此咱們這裏只作簡單討論,將用類的屬性的 hash 值再次進行異或操做計算新的 hash 值便可。仍是以咱們的 Name 類爲例:
class Name: Comparable,Hashable {
...
var hashValue: Int {
return self.firstName.hashValue ^ self.lastName.hashValue
}
}
接下來咱們就將它做爲字典的 key 了:
let john = Name(firstName: "John", lastName: "Smith")
let johnClone = Name(firstName: "John", lastName: "Smith")
let jack = Name(firstName: "Jack", lastName: "Smith")
let mary = Name(firstName: "Mary", lastName: "Williams")
var nameMap:[Name: String] = [:]
nameMap[john] = "Manager"
nameMap[jack] = "Stuff"
這片文章介紹了 Swift 中的三個協議 Equatable
,Comparable
,Hashable
,雖然咱們平時不會太注意這幾個協議,但在咱們的開發工做中,他們卻無時無刻再也不起着做用,咱們的 if 語句,排序操做,字典和數組的操做,都和這幾個協議相關。 而 Swift 中的大部分類,基本都實現了這幾個協議。能夠說雖然咱們常常遺忘他們,但它們又無時無刻不在咱們左右。瞭解他們以後,必定會對咱們大有幫助。
更多文章請訪問: www.theswiftworld.com
更多好文,掃碼關注微信公衆號: