swift 協議的使用方法和場景
協議是swift一個重要的部分,類似於Java中的介面,但是還不是很一樣。相比較OC,swift中協議更加靈活,它可以應用在很多場景,使整個專案的框架結構更加易於延展。
一、什麼場景下使用協議
協議與類類似,可以被繼承,當繼承某個協議之後就要給協議所定義的屬性賦值並且實現協議中的方法。
既然協議與類這麼類似,那我們為什麼不全部用類來實現,為什麼還要用到協議?
舉個簡單的例子,有一隻貓和狗,他們都屬於寵物,用類去實現就要這樣操作,定義一個父類叫做寵物,裡面有餵食和玩耍兩個方法,貓和狗都繼承與寵物這個父類。這樣操作自然可以實現,但是要知道,貓和狗不都是寵物,這裡把寵物定義成父類就不是很合適,這裡應該把寵物定義成協議就相對合適很多啦
二、協議的使用方法
1、協議定義
// 協議定義通過關鍵字protocol
protocol SomeProtocol {
// 協議定義
}
// 協議可以繼承一個或者多個協議
protocol SomeProtocol2 :SomeProtocol {
// 協議定義
}
// 結構體實現協議
struct SomeStructure : SomeProtocol,SomeProtocol2 {
// 結構體定義
}
// 類實現協議和繼承父類,協議一般都寫在父類後面
class SomeSuperclass {
// 父類定義
}
class SomeClass :SomeSuperclass ,SomeProtocol,SomeProtocol2 {
// 子類定義
}
2、協議的屬性
協議不指定是否該屬性應該是一個儲存屬性或者計算屬性,它只指定所需的屬性名稱和讀寫型別。屬性要求總是宣告為變數屬性,用var關鍵字做字首。
protocol ClassProtocol {
static var present:Bool { get set } // 要求該屬性可讀可寫,並且是靜態的
var subject :String { get } // 要求該屬性可讀
var stname :String { get set } // 要求該屬性可讀可寫
}
// 定義類來實現協議
class MyClass :ClassProtocol {
static var present = false // 如果沒有實現協議的屬性要求,會直接報錯
var subject = "Swift Protocols" // 該屬性設定為可讀可寫,也是滿足協議要求的
var stname = "Class"
func attendance() -> String {
return "The \(self.stname) has secured 99% attendance"
}
func markSScured() -> String {
return "\(self.stname) has \(self.subject)"
}
}
// 建立物件
var classa = MyClass()
print(classa.attendance()) // 結果:The Class has secured 99% attendance
print(classa.markSScured()) // 結果:Class has Swift Protocols
3、協議普通方法實現
協議可以要求指定例項方法和型別方法被一致的型別實現。這些方法被寫為協議定義的一部分,跟普通例項和型別方法完全一樣,但是沒有大括號或方法體。可變引數是允許的,普通方法也遵循同樣的規則,不過不允許給協議方法引數指定預設值。
// 定義協議,指定方法要求
protocol RandomNumberGenerator {
func random() -> Double // 實現該協議,需要實現該方法
}
class LinearCongruentialGenerator :RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
// 實現協議方法
func random() -> Double {
lastRandom = ((lastRandom * a + c) % m)
return lastRandom / m
}
}
let generator = LinearCongruentialGenerator()
print("隨機數:\(generator.random())") //結果:隨機數: 0.37464991998171
print("另一個隨機數:\(generator.random())") //結果:另一個隨機數: 0.729023776863283
4、協議中實現建構函式
協議SomeProtocol中不光可以宣告方法/屬性/下標,還可以宣告構造器,但在Swift中,除了某些特殊情況外,構造器是不被子類繼承的,所以SomeClass中雖然能夠保證定義了協議要求的構造器,但不能保證SomeClass的子類中也定義了協議要求的構造器。所以我們需要在實現協議要求的構造器時,使用required關鍵字確保SomeClass的子類必須也得實現這個構造器。
protocol TcpProtocol {
// 初始化構造器要求
init(aprot :Int)
}
class TcpClass :TcpProtocol {
var aprot: Int
// 實現協議的初始化要求時,必須使用required關鍵字確保子類必須也得實現這個構造器
required init(aprot: Int) {
self.aprot = aprot
}
}
var tcp = TcpClass(aprot: 20)
print(tcp.aprot) // return:20
三、 使用例項
開頭說的寵物貓和寵物狗的例子,利用協議可以這樣實現,聲名個動物的父類,然後讓貓和狗class都繼承與動物class。在定義一個寵物的屬性,裡面有玩耍和餵食兩個方法,讓貓和狗都繼承與寵物協議,實現程式碼如下:
protocol Pet {
func playWith()
func fed(food : String)
}
class Animal{
var name : String = ""
var birthPlace : String = ""
init(name: String,birthPlace:String) {
self.name = name
self.birthPlace = birthPlace
}
}
class Dog: Animal, Pet{
func playWith() {
print("狗狗在玩")
}
func fed(food: String) {
if food == "骨頭" {
print("狗狗Happy")
}
else {
print("狗狗Sad")
}
}
}
class Cat: Animal, Pet {
func playWith() {
print("貓貓在玩")
}
func fed(food: String) {
if food == "魚" {
print("貓貓Happy")
}
else {
print("貓貓Sad")
}
}
}
let dog = Dog(name:"狗狗小黑", birthPlace: "北京")
dog.playWith()
dog.fed(food:"骨頭")
let cat = Cat(name:"貓貓小白", birthPlace:"上海")
cat.playWith()
cat.fed(food: "魚")
注意:同時繼承父類和協議的時候,父類要寫在前面
四、typealias與協議結合的使用
typealias 的作用是給型別進行擴充套件,它與協議放在一起使用會碰撞出不一樣的火花
1、typealias的基本使用
extension Double {
var km : Length{ return self * 1000.0 }
var m : Length{ return self }
var cm : Length{ return self / 100 }
}
這裡對Double型別進行擴充套件
let runDistance:Length = 3.14.km //3140
2、typealias結合協議使用
定義一個協議,代表重量,但是它的型別要根據繼承與它的類或結構體來定義,協議程式碼如下:
protocol WeightCalculable {
associatedtype WeightType
var weight:WeightType{get}
}
這裡weight屬性的型別就拋了出來,便於繼承協議的類或結構體來定義
class iPhone7 : WeightCalculable {
typealias WeightType = Double
var weight: WeightType {
return 0.114
}
}
class Ship : WeightCalculable {
typealias WeightType = Int
let weight: WeightType
init(weight: Int) {
self.weight = weight
}
}
這裡定義了兩個類,一個是iPhone7,一個是Ship,都繼承於協議WeightCalculable,但是weight的型別大不相同。
iPhone7的weight屬性是Double型別的,Ship的weight屬性是Int型別的。
extension Int {
typealias Weight = Int
var t:Weight {
return 1_000*self
}
}
let ship = Ship(weight:4_637.t)
最後這段程式碼,用於擴充Int型別,自定義了t欄位來代表噸
五、系統協議的使用
我們還可以繼承於系統協議來定義系統方法,這裡簡單極少介紹三種常用系統協議
1、Equatable協議用於自定義”==”來實現操作
class Person:Equatable , Comparable, CustomStringConvertible {
var name:String
var age:Int
init(name:String,age:Int) {
self.name = name
self.age = age
}
var description: String {
return"name: "+name + ",age:" + String(age)
}
}
func == (left: Person, right: Person) ->Bool{
return left.name == right.name && left.age == right.age
}
let personA = Person(name:"a",age:9)
let personB = Person(name:"a",age:10)
personA == personB
personA != personB
注意:func == 方法要緊跟協議下面寫,否則編譯器會報錯
2、Comparable協議用於自定義比較符號來使用的
func <(left: Person, right: Person) ->Bool{
return left.age < right.age
}
let personA = Person(name:"a",age:9)
let personB = Person(name:"a",age:10)
注意,在定義比較符號後,很多方法也會同時修改便於我們使用,例如排序方法
let person1 = Person(name:"a",age:9)
let person2 = Person(name:"a",age:12)
let person3 = Person(name:"a",age:11)
var arrPerson = [person1,person2,person3]
arrPerson .sort()
//此時arrPerson : [person1,person3,person2]
3、CustomStringConvertible協議用於自定義列印
class Person:Equatable , Comparable, CustomStringConvertible {
var name:String
var age:Int
init(name:String,age:Int) {
self.name = name
self.age = age
}
var description: String {
return"name: "+name + ",age:" + String(age)
}
}
重寫description 講自定義列印格式return出來
print(person1)
//name: a,age:9
協議是swift非常重要的一部分,蘋果甚至為了它單獨出來——–面向協議程式設計,利用協議的優點和靈活性可以使整個專案結構更加靈活,擁有更加易於延展的架構。