Swift學習(十六):協議(protocol,static,class,mutating,init,init?,init!,繼承,協議組合,as?,as!,Any,AnyObject,Self)

協議(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())

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章