十五.可選協議
1. Swift還支持在協議中定義定義可選成員要求(包括屬性、方法、下標,不包括構造器)----是可選成員要求,不是可選成員。可選成員要求的意思是:該協議的實現類型既可以實現這些協議成員,也可以不實現這些協議成員。
2. 只要在協議成員前添加關鍵optional關鍵字即可定義可選協議。
3. 由於協議的實現者可以不實現可選協議的要求,因此程序面向對象編程時,調用可選協議的可選成員時,推薦使用可選鏈(需要在屬性、方法、下標後添加問號)調用,這樣可以避免當該成員沒有被實現時導致的運行時錯誤,調用可選成員的返回值總是可選值。
4.由於可選協議主要是爲了保持與Objective-C協議的兼容性,因此可選協議必須添加@objc修飾----@objc用於聲明類、協議可以被Objective-C調用。即使該協議不打算被Objective-C調用,也必須添加@objc修飾。不僅如此,可選協議 只能 被類實現,不能被枚舉、結構體實現,所以,可選協議一定是唯類協議。
5. 舉個栗子:
@objc protocol MyProtocol
{
//定義可選屬性
optional var status : String {get}
//定義可選方法
optional func test(val : Int)
//定義可選下標
optional subscorpt(ids : Int) -> {get}
}
上面定義了一個協議,使用了@objc修飾,因此該協議一定是一個唯類協議,而且該協議可以包含可選成員。
既然MyProtocol協議的所有成員都是可選的,因此可以定義一個空類來實現該協議:
//定義一個空類,實現MyProtocol協議,但並不實現該協議的任何成員
class EmptyClass : MyPtorocol
{}
6. 關聯到3中的知識點,在使用可選協議調用可選屬性、可選下標時可以不使用可選鏈----不管程序使用使用可選鏈,調用可選成員總是返回可選值。 使用可選協議調用可選方法時,Swift要求必須 使用可選鏈。
十六.輸出實例和Printable協議
1. 假設有一個Person,實例爲:var peron = Person(),用print(person)輸出Person實例後,將看到:PrintInstance.Person,這個結果沒有太大的意義。
2. 如果希望輸出實例時,能真正看到該實例的內部狀態,可以讓該實例實例實現Prinable協議,並實現呢協議中的description只讀屬性。
3. description只讀屬性是一個非常特殊的屬性,它是一個“自我描述”屬性,該屬性的返回值通常用於實現這樣一個功能:當程序直接輸出該實例時,系統將輸出該實例的“自我描述”信息,用於告訴外界該實例具有的狀態信息。
4. 舉個栗子:
class Person : Printable
{
var name : String
var age : Int
init(name : String, age : Int)
{
self.name = name
self.age = age
}
//Printable中的只讀屬性
var description : String
{
return "msg: (\name) and (\age)"
}
}
則下面兩個代碼的結果完全相同:
print(person)
print(person.description)
十七.使用自定義類型作爲字典的key
1. 前面學習字典的時候,總是使用系統的Int、String等作爲key。這裏將要學習使用自定義類型作爲字典的key。
2. 在字典中,不允許出現相同的兩個key。只要兩個key滿足一下兩個條件,字典就會認爲他們相等:
(1)兩個key通過==比較返回true;
(2)兩個key的hashValue屬性返回相等的整數。
3. 但是自定義類型的實例無法通過==進行比較,自定義類型的實例也沒有hashValue屬性,爲了讓自定義類型滿足上述兩個條件,自定義類型必須進行如下改造:
(1)讓自定義類型實現Equatable協議,並重載==比較運算符,是的自定義類型的實例可以通過==進行比較。
(2)讓自定義類型實現Hashable協議,並實現協議中的hashValue只讀屬性。實現hashValue只讀屬性時,應該和重載的==保持一致。也就是說,當兩個實例通過==比較返回true時,兩個實例的hashValue也應該相等。----實現Equatable協議,並重載==運算符實際上就是重新定義了一個名爲==的函數,方法中的內容由自己提供認爲相等的標準。
4. Hashable、Equatable協議都是Swift提供的協議,而且Hashable協議是Equatable的子協議,因此自定義類型只要實現Hashable協議即可。
5. 通常而言,正確地重載==運算符應該滿足以下條件:
(1)自反性:對任意x,x==x應該返回true;
(2)對稱性:對任意x,y,如果x==y返回true,那麼y==x也返回true
(3)傳遞性:對任意x,y,z,如果x==y返回true,y==z返回true,則x==z一定返回true。
(4)一致性:對任意x和y,如果實例中用於等價比較的關鍵信息沒有改變,那麼無論x==y調用多少次,返回的結果都應該保持一致,
6.舉個栗子:
class User : Equatable, Printable
{
var name : String
var pwd : String
var age : Int
init(name : String, pwd : String, age : Int)
{
self.name = name
self.pwd = pwd
self.age = age
}
var hashValue : Int
{
//根據name,pwd的hashValue來計算User實例的hashValue
//考慮到數據可能溢出,故此處採用溢出運算符
return name.hashValue &* 31 &+ pwd.hashValue
}
var description : String
{
return "User{\(name),\(pwd),\(age)}"
}
//重載==運算符
func == (lhs : User, rhs : Usr)
{
return lhs.name == rhs.name && lhs.pwd == rhs.pwd
}
}
上面的User類滿足了上面3中所說的兩個條件,因此這個類的實例可以作爲字典的key。