協議(Protocol)
- 協議可以用來定義方法、屬性、下標的聲明,協議可以被枚舉、結構體、類遵守(多個協議之間用逗號隔開)
- 協議中定義方法時不能有默認參數值
- 默認情況下,協議中定義的內容必須全部都實現
- 也有辦法可以辦到只實現部分內容
協議中的屬性
- 協議中定義屬性時必須用var關鍵字
- 實現協議時的屬性權限要不小於協議中定義的屬性權限
- 協議定義get、set,用var存儲屬性或get、set計算屬性去實現
- 協議定義get,用任何屬性都可以實現
這裏的var x: Int = 0可以看作是實現了set和get,因爲可以賦值和取值;let y: Int = 0由於不能修改值,可以看作只實現了get,也可以使用下圖的計算屬性方式實現:
static、class
爲了保證通用,協議中必須用static定義類型方法、類型屬性、類型下標
實現的時候使用class則代表方法可以被繼承,static則代表方法不能被繼承
mutating
- 只有將協議中的實例方法標記爲mutating,才允許結構體、枚舉的具體實現修改自身內存
- 類在實現方法時不用加mutating,枚舉、結構體才需要加mutating
protocol中不加mutating,而結構體里加了mutating,結構體會報錯,protocol對於結構體而言需要加mutating
init
- 協議中還可以定義初始化器init
- 非final類實現時必須加上required
加required的原因是因爲這樣可以要求繼承自Point的類都必須實現init,從而達到所有遵守Drawable協議的類都實現init,final不需要加是因爲加final的類不允許被繼承。
- 如果從協議實現的初始化器,剛好是重寫了父類的指定初始化器,那麼這個初始化必須同時加required、override
init、init?、init!
- 協議中定義的init?、init!,可以用init、init?、init!去實現
- 協議中定義的init,可以用init、init!去實現
protocol Liveable {
init()
init?(age: Int)
init!(no: Int)
}
class Person: Liveable {
required init() {
}
//init()第二種寫法
// required init!() { }
required init?(age: Int) {
}
//init?(age: Int)第二種寫法
// required init(age: Int) { }
// required init!(age: Int){ }
required init!(no: Int) {
}
//init!(no: Int)第二種寫法
// required init(no: Int){ }
// required init?(no: Int){ }
}
協議的繼承
- 一個協議可以繼承其他協議
可以看出,遵守Livable協議的Person類必須實現Runnable和Livable都有的方法。
協議組合
- 協議組合,可以包含一個類類型(最多一個)
可以給協議組合起別名:
CaseIterable
- 讓枚舉遵守CaseIterable協議,可以實現遍歷枚舉值
CustomStringConvertible
- 遵守CustomStringConvertible協議,可以自定義實例的打印字符串
- print調用的是CustomStringConvertible協議的description
- debugPrint,po調用的是CustomDebugStringConvertible協議的debugDescription
Any、AnyObject
- Swift提供了2種特殊的類型:Any、AnyObject
- Any:可以代表任意類型(枚舉、結構體、類,也包括函數類型)
- AnyObject:可以代表任意類類型(在協議後面寫上: AnyObject代表只有類能遵守這個協議)
stu屬於Any類型,可以賦任何值:
[Any]():創建可以存放任何類型的數組
is、as?、as!、as
- is用來判斷是否爲某種類型,as用來做強制類型轉換
is判斷類型:
as做強制類型轉換:
(stu as? Student)?.study():第一個?代表stu可能強制轉化爲Student,也可能失敗,第二個?代表可選類型要調用方法,需要加?
(stu as? Student)!.study() 等同於 (stu as! Student).study()
當確定一定會轉換成功用as,否則用as?
Int("123")一定可以轉化爲Any,所以用as
10一定可以轉換爲Double,所以用as
X.self、X.Type、AnyClass
- X.self是一個元類型(metadata)的指針,metadata存放着類型相關信息
對象的指針指向的堆內存的前8個字節存放着類型相關信息,可以把前8個字節認爲是元類型的指針,指向元類型metadata
class Person {
}
var p: Person = Person()
var pType: Person.Type = Person.self
反彙編上面代碼,查看pType存儲的到底是什麼:
根據兩個斷點處的rax對比我們可以看出,代表p對象內存地址的第二個rax中的前8個字節與代表pType的第一個rax相同,說明pType存儲的是p對象的metadata。
- X.self屬於X.Type類型
由上圖看出,由於Student繼承自Person,所以pType = Student.self也成立,好比多態的父類指針指向子類對象,但是不繼承自Person的類不可以這麼寫
- 在Swift底層方法裏,有這麼個定義
代表着AnyClass就是AnyObject.Type,是任意類類型的Type
等價於
- type(of: )獲取元類型的指針
元類型的應用
爲什麼Animal的init(){ }要用required修飾?因爲這是要求子類必須有的,實現的方法,萬一子類重寫init相關的方法,就會造成子類裏不會自動繼承父類的init,這時添加required,是強制子類實現init()方法,這樣就會保證子類可以調用init()方法
class_getInstanceSize:表示對象實際用到的內存大小,也就是存儲屬性需要的最小值
class_getSuperclass:獲取父類,我們可以看到Person雖然沒繼承任何類,但它有一個要隱藏的基類Swift._SwiftObject
- 從結果可以看得出來,Swift還有個隱藏的基類:Swift._SwiftObject
- 可以參考Swift源碼:https://github.com/apple/swift/blob/master/stdlib/public/runtime/SwiftObject.h
Self
- Self代表當前類型
Self.count 等同於Person.count
- Self一般用作返回值類型,限定返回值跟方法調用者必須是同一類型(也可以作爲參數類型)
protocol Runnable {
func test () -> Self
}
class Person : Runnable {
required init() { }
func test() -> Self {
return type(of: self).init()
}
}
class Student: Person { }
var p = Person()
//TestSwift.Person
print(p.test())
var stu = Student()
//TestSwift.Student
print(stu.test())