Swift之協議

本文首發於我的博客html

前言

協議,有關開發經驗的應該都不陌生,不少語言中都有協議,可是相對來講,Swift中的協議更增強大,靈活。git

  • Swift中協議能夠用來定義方法、屬性、下標的聲明,協議能夠被枚舉、結構體、類遵照(多個協議之間用逗號隔開)
//協議
protocol Drawable {
    //方法
    func draw()
    //可讀可寫屬性
    var x: Int { get set }
    //只讀屬性
    var y: Int { get }
    // 下標
    subscript(index: Int) -> Int { get set }
}

複製代碼

TestClass準守多個協議github

protocol Test1 { }
protocol Test2 { }
protocol Test3 { }
class TestClass : Test1, Test2, Test3 { }
複製代碼

須要注意的是

  • 協議中定義方法時不能有默認參數值
  • 默認狀況下,協議中定義的內容必須所有都實現
  • 也有辦法辦到只實現部份內容,後面會說

協議中的屬性

  • 協議中定義屬性時必須用var關鍵字
  • 實現協議時的屬性權限要不小於協議中定義的屬性權限
  • 協議定義get、set,用var存儲屬性或get、set計算屬性去實現
  • 協議定義get,用任何屬性均可以實現

eg:有協議Drawable,裏面有方法draw,以及可讀可寫屬性x,只讀屬性y,下標。其中屬性必須用var關鍵字spring

//協議
protocol Drawable {
    func draw() //方法
    var x: Int { get set } //可讀可寫 屬性用var
    var y: Int { get } //只讀  屬性用var
    subscript(index: Int) -> Int { get set } //下標
}
複製代碼

當實現的時候,有以下的方式,編程

class Person : Drawable {
    var x: Int = 0 //用var的存儲屬性
    let y: Int = 0 //let實現只讀屬性
    func draw() {
        print("Person draw")
    }
    subscript(index: Int) -> Int {
        set { }
        get { index }
    }
}
複製代碼

固然了。也能夠寫成以下這種swift

class Person : Drawable {
    var x: Int { //用計算屬性
        get { 0 }
        set { }
    }
    var y: Int { 0 } //var實現只讀屬性
    func draw() { print("Person draw") }
    subscript(index: Int) -> Int {
        set { }
        get { index }
    }
}
複製代碼

static、class

  • 爲了保證通用,協議中必須用static定義類型方法、類型屬性、類型下標
    • 由於class只能用在類中,不能用於結構體等。因此爲了通用,用static
    • 可是實現的時候,能夠用class,也能夠用static,具體看本身的狀況 eg:
protocol Drawable {
	//這裏必須用static
    static func draw()
}
class Person1 : Drawable {
	//這裏能夠用class
    class func draw() {
        print("Person1 draw")
    }
}
class Person2 : Drawable {
	//這裏也能夠用static
    static func draw() {
        print("Person2 draw")
    }
}
複製代碼

mutating

關於mutating能夠參考Swift之方法數組

  • 只有將協議中的實例方法標記爲mutating
    • 才容許結構體、枚舉的具體實現修改自身內存
    • 類在實現方法時不用加mutating,枚舉、結構體才須要加mutating

eg:bash

protocol Drawable {
    mutating func draw()
}

class Size : Drawable {
    var width: Int = 0
    func draw() {
        width = 10
    }
}

struct Point : Drawable {
    var x: Int = 0
    mutating func draw() {
        x = 10
    }
}
複製代碼

init

  • 協議中還能夠定義初始化器init
    • 非final類實現時必須加上required

能夠這麼理解,若是定義的類,有子類,那麼子類必須準守初始化器init,因此加上關鍵字required,可是,若是一個被final修飾的類。就不用加上required.由於被final修飾的類不能被其餘類繼承。app

關於final可參考Swift之繼承ide

eg: 有協議Drawable,裏面定義了初始化器init,類Point遵照這個協議,因此在init 前面加了關鍵字 required,這樣,繼承類Point的子類都要實現這個方法,可是類Size沒子類。由於加了關鍵字final,這個類不能被繼承。因此init前面不用加required

protocol Drawable {
    init(x: Int, y: Int)
}
class Point : Drawable {
    required init(x: Int, y: Int) { }
}
final class Size : Drawable {
    init(x: Int, y: Int) { }
}
複製代碼
  • 若是從協議實現的初始化器,恰好是重寫了父類的指定初始化器
    • 那麼這個初始化必須同時加required、override

eg:

protocol Livable {
    init(age: Int)
}

class Person {
    init(age: Int) { }
}

class Student : Person, Livable {
    required override init(age: Int) {
        super.init(age: age)
    }
}
複製代碼

init、init?、init!

  • 協議中定義的init?、init!,能夠用init、init?、init!去實現
  • 協議中定義的init,能夠用init、init!去實現

eg:

//協議
protocol Livable {
    init()
    init?(age: Int)
    init!(no: Int)
}

//類
class Person : Livable {
	
	//下面兩種均可以實現init()
    required init() { }
    // required init!() { }
    
    //下面3種均可以實現init?(age: Int)
    required init?(age: Int) { }
    // required init!(age: Int) { }
    // required init(age: Int) { }
    
     //下面3種均可以實現 init!(no: Int)
    required init!(no: Int) { }
    // required init?(no: Int) { }
    // required init(no: Int) { }
}

複製代碼

協議的繼承

  • 一個協議能夠繼承其餘協議

eg

// 協議Runnable
protocol Runnable {
    func run()
}

// 協議Livable 繼承協議 Runnable
protocol Livable : Runnable {
    func breath()
}

class Person : Livable {
    func breath() { }
    func run() { }
}

複製代碼

協議組合

  • 協議組合,多個協議組合在一塊兒,並且能夠包含1個類類型(最多1個)

eg: 兩個協議LivableRunnable,類Person

protocol Livable { } n
protocol Runnable { }
class Person { }
複製代碼

下面定義了fn0,接收參數必須是Person或者其子類的實例。fn1接收參數必須遵照Livable協議的實例,其餘的能夠自行看代碼

// 接收Person或者其子類的實例
func fn0(obj: Person) { }


// 接收遵照Livable協議的實例
func fn1(obj: Livable) { }


// 接收同時遵照Livable、Runnable協議的實例
func fn2(obj: Livable & Runnable) { }


// 接收同時遵照Livable、Runnable協議、而且是Person或者其子類的實例
func fn3(obj: Person & Livable & Runnable) { }


typealias RealPerson = Person & Livable & Runnable
// 接收同時遵照Livable、Runnable協議、而且是Person或者其子類的實例
func fn4(obj: RealPerson) { }
複製代碼

CaseIterable

  • 讓枚舉遵照CaseIterable協議,能夠實現遍歷枚舉值
// 枚舉Season遵照協議CaseIterable
enum Season : CaseIterable {
    case spring, summer, autumn, winter
}

// 取出全部的case
let seasons = Season.allCases
print(seasons.count) // 4

// 能夠遍歷
for season in seasons {
    print(season)
} // spring summer autumn winter
複製代碼

CustomStringConvertible

  • 遵照CustomStringConvertible協議,能夠自定義實例的打印字符串

eg: Person類遵照了CustomStringConvertible協議,能夠再內部自定義打印description

class Person : CustomStringConvertible {
    var age: Int
    var name: String
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
    var description: String {
        "age=\(age), name=\(name)"
    }
}
var p = Person(age: 10, name: "Jack")
print(p) // age=10, name=Jack

複製代碼

Any、AnyObject

  • Swift提供了2種特殊的類型:Any、AnyObject
    • Any:能夠表明任意類型(枚舉、結構體、類,也包括函數類型)
    • AnyObject:能夠表明任意類類型(在協議後面寫上: AnyObject表明只有類能遵照這個協議)

eg: stu屬於Any類型,能夠賦值爲字符串或者對象等

var stu: Any = 10
stu = "Jack"
stu = Student()
複製代碼

eg: 建立1個能存聽任意類型的數組

// 建立1個能存聽任意類型的數組
// 第一種寫法
// var data = Array<Any>()
// 第二種寫法
var data = [Any]()
data.append(1)
data.append(3.14)
data.append(Student())
data.append("Jack")
data.append({ 10 })
複製代碼

is、as?、as!、as

  • is用來判斷是否爲某種類型,as用來作強制類型轉換

eg: 定義協議Runnable,類Person和類Student

protocol Runnable { func run() }
class Person { }
class Student : Person, Runnable {
    func run() {
        print("Student run")
    }
    func study() {
        print("Student study")
    }
}
複製代碼

使用is 的時候

var stu: Any = 10
print(stu is Int) // true
stu = "Jack"
print(stu is String) // true
stu = Student()
print(stu is Person) // true
print(stu is Student) // true
print(stu is Runnable) // t
複製代碼

使用as 的時候,若是轉換失敗,後面都不執行。轉換成功,後面才繼續執行

var stu: Any = 10
(stu as? Student)?.study() // 沒有調用study
stu = Student()
(stu as? Student)?.study() // Student study
(stu as! Student).study() // Student study
(stu as? Runnable)?.run() // Student run
複製代碼

X.self、X.Type、AnyClass

  • X.self是一個元類型(metadata)的指針,metadata存放着類型相關信息
  • X.self屬於X.Type類型
// 定義類Person
class Person { }
// 定義類Student 繼承 Person
class Student : Person { }
//perType類型是 Person.Type
var perType: Person.Type = Person.self
//stuType類型是 Student.Type
var stuType: Student.Type = Student.self
// Student.self能夠賦值給perType
perType = Student.self


// anyType能夠是任何類型
var anyType: AnyObject.Type = Person.self
anyType = Student.self


public typealias AnyClass = AnyObject.Type
//anyType2能夠是任何類型
var anyType2: AnyClass = Person.self
anyType2 = Student.self
複製代碼

元類型的應用

eg:Cat類,Dog類,Pig類,都繼承自Animal類,咱們想同時去初始化,可以使用下面的代碼

class Animal { required init() { } }
class Cat : Animal { }
class Dog : Animal { }
class Pig : Animal { }
func create(_ clses: [Animal.Type]) -> [Animal] {
    var arr = [Animal]()
    for cls in clses {
        // 根據元類型初始化
        arr.append(cls.init())
    }
    return arr
}
// 這裏傳入Cat,Dog,Pig進行初始化
print(create([Cat.self, Dog.self, Pig.self]))
複製代碼

注意上面的required不能省略

  • 在OC、Java等語言中,任何一個類最終都要繼承自某個基類,
  • 在Swift中沒有這個規定,若是一個類不繼承任何一個類,那這個類就是基類

實際上,真的如此麼,真的不繼承任何類麼?

class Person {
    var age: Int = 0
}
class Student : Person {
    var no: Int = 0
}
print(class_getInstanceSize(Student.self)) // 32
print(class_getSuperclass(Student.self)!) // Person
print(class_getSuperclass(Person.self)!) // Swift._SwiftObject
複製代碼
  • 從結果能夠看得出來,Swift還有個隱藏的基類:Swift._SwiftObject
  • 能夠參考Swift源碼

Self

  • Self通常用做返回值類型,限定返回值跟方法調用者必須是同一類型(也能夠做爲參數類型)

有點相似OC中的instanType的感受。eg:

protocol Runnable {
    func test() -> Self
}
class Person : Runnable {
    required init() { }
    func test() -> Self { type(of: self).init() }
}
class Student : Person { }
複製代碼

調用時候

var p = Person()
// 輸出Person
print(p.test())


var stu = Student()
// 輸出Student
print(stu.test())
複製代碼

參考資料:

Swift官方源碼

從入門到精通Swift編程

相關文章
相關標籤/搜索