天天看點

Swift學習筆記-009協定(瘋狂swift講義第二版)

1.協定的文法(十分類似java的接口)

修飾符 protocol 協定名:父協定1,父協定2,…{

//協定内容

}

2.協定的使用

結構體使用協定

struct 結構體名:協定1,協定2…{

//實作協定

}

class 類名:父類,協定1,協定2,…{

//類語句及實作協定

}

3.協定中添加屬性

一般格式

[static] var 屬性名:類型{get [set]}

static定義類屬性,這裡不可以用class替代

get,set隻需填寫即可,不需要寫其實作,set可有可無,如果有set則要求實作者提供與之比對的讀寫屬性

4.結構體和類中使用協定
protocol Strokable {
    var strokeWidth:Double{get set}
}

enum Color{
    case Red,Green,Blue,Yellow,Cyan
}
protocol Fullable {
    var fullColor:Color?{get set}
}

protocol HasArea:Fullable,Strokable {
    var area:Double {get}
}
protocol Mathable {
    static var pi:Double{get}
    static var e:Double{get}
}

//實作以上接口

struct Rect:HasArea,Mathable{
    var fullColor: Color?
    var strokeWidth: Double=0.0
    //+++++++++++++++++++
    
    var area: Double{
        get{ return width*height}
    }
    //+++++++++++++++++++
    
    static var pi: Double=3.1415926
    static var e: Double=2.718
    //+++++++++++++++++++
    var width:Double
    var height:Double
    init(width:Double,height:Double) {
        self.width=width
        self.height=height
    }
    
}

class Circle:HasArea,Mathable{
    var radius:Double
    init(radius:Double) {
        self.radius=radius
    }
    var fullColor: Color?
    var strokeWidth: Double=0.0
    var area: Double{
        get{
            return Circle.pi*radius*radius//實作協定是,這個變量被繼承下來,接下來也将多這個屬性指派,是以應該使用Circle.pi而不是使用Mathable.pi。如果使用Mathable.pi則會報錯誤:Static member 'pi' cannot be used on protocol metatype 'Mathable.Protocol'
        }
    }
    static var pi:Double=3.1415926
    static var e:Double=2.714
}

var rect = Rect(width: 4.5, height: 4.0)
print(rect.area)
rect.fullColor=Color.Red
rect.strokeWidth=1.0
print(rect.fullColor!)
print(rect.strokeWidth)

var circle = Circle(radius: 2.0)
circle.fullColor=Color.Green
circle.strokeWidth=0.5
print(circle.fullColor!)
print(circle.strokeWidth)
           

列印結果

18.0

Red

1.0

Green

0.5

5.協定中的方法

支援形參可變的方法

protocol Eatable {
    func taste()
    static func test(msg:String...)
}
//結構體實作協定
struct Pie:Eatable{
    var weight:Double
    func taste() {
        print("\(weight) 重量的餅幹才能吃飽")
    }
    
    static func test(msg: String...) {
        print("實作關于餅幹的類方法")
        for mymsg in msg{
            print("here is the para \(mymsg)")
        }
    }

}
//類實作協定
class Apple: Eatable {
  
    
    var name:String
    init(name:String) {
        self.name=name
    }
    func taste() {
        print("\(name) is full of water")
    }
    static func test(msg: String...) {
        for mymsg in msg{
            print("個數可變的形參\(mymsg)")
        }
    }
}

//執行方法
Pie.test(msg: "餅幹1","餅幹2","餅幹3")
var pie=Pie(weight: 2.3)
pie.taste()
Apple.test(msg: "冰糖心蘋果","紅富士蘋果","其他蘋果")
var apple=Apple(name: "本地蘋果")
apple.taste()
           

列印結果

實作關于餅幹的類方法

here is the para 餅幹1

here is the para 餅幹2

here is the para 餅幹3

2.3 重量的餅幹才能吃飽

個數可變的形參冰糖心蘋果

個數可變的形參紅富士蘋果

個數可變的形參其他蘋果

本地蘋果 is full of water

5.協定中的可變方法
protocol Incrementable {
    mutating func incremetByDelta(delta:Double)
}
//使用結構體實作接口
struct FKRange:Incrementable{
    var start:Double
    var length:Double
    mutating func incremetByDelta(delta:Double){
        self.length+=delta
    }
}
//使用類實作接口
class Circle:Incrementable{
    var radius:Double
    init (radius:Double){
        self.radius=radius
    }
    func incremetByDelta(delta:Double){
        self.radius+=delta
    }
}

//執行
var range = FKRange(start: 2.0, length: 10.0)
range.incremetByDelta(delta: 5.0)
print(range.length)
var circle=Circle(radius: 5.0)
circle.incremetByDelta(delta: 5.0)
print(circle.radius)
           

列印結果

15.0

10.0

6.協定指定的下标

subscript(形參清單)->傳回值{get [set]}

protocol Mathable {
    subscript (idx:Int)->Int{get}
    subscript(a:Int,b:Int)->Int{get}
}

//結構體實作協定
struct LinearStruct:Mathable{
    var factor:Int
    subscript(idx:Int)->Int{
        get{
            return factor*idx
        }
        set{
            print("執行LinearStruct的下标指派")
        }
    }
    subscript(a:Int,b:Int)->Int{
        return factor*a+b
    }
}

//類實作協定
class Quadratic: Mathable {
    var factor:Int
    init(factor:Int){
        self.factor=factor
    }
    //實作下标
    subscript(idx:Int)->Int{
        return factor*factor*idx
    }
    subscript(a:Int,b:Int)->Int{
        return factor*factor*a+b
    }
}

//執行
var q=Quadratic(factor: 5)
print(q[4])
print(q[4,6])

var line=LinearStruct(factor: 5)
print(line[4])
print(line[4,6])
           

列印結果

100

106

20

26

7.協定中指定構造器

類實作協定時,可以使用指定構造器也可以是用便利構造器,此外還需注意兩點

1、使用類實作協定,并實作構造器,必須使用required修飾符,除非構造器之前使用了final

2、如果類在實作協定并實作構造器時,如果子類重寫了父類的構造器,必須同時使用required override修飾

protocol Initable{//接口的命名好像都喜歡用able
    //定義兩個構造器
    init(name:String)
    init(name:String,weight:Double)
}
//d使用結構體實作協定
struct Bag:Initable{
    var name:String
    var weight:Double
    init(name:String){
        self.name=name
        self.weight=0.0
    }
    init(name: String, weight: Double) {
        self.name=name
        self.weight=weight
    }
}

//使用類實作協定
//先定義一個父類
class Fruit{
    var name:String
    init(name:String) {
        self.name=name
    }
}
//在子類中實作協定
class Apple:Fruit,Initable{
    var weight:Double
    //使用required override
    required override init(name: String) {
        self.weight=0.0
        super.init(name: name)
    }
    //使用便利構造器實作協定的構造器
    required convenience init(name: String, weight: Double) {
        self.init(name: name)
        self.weight=weight

    }
}

//執行
var apple1 = Apple(name: "紅富士")
var apple2=Apple(name: "冰糖心", weight: 2.3)
print("\(apple2.name)----\(apple2.weight)")

var bag1=Bag(name: "書包")
var bag2=Bag(name: "旅行包", weight: 20.3)
print("\(bag2.name)----\(bag2.weight)")
           

列印結果

冰糖心----2.3

旅行包----20.3

8.使用協定作為類型

使用協定聲明變量

協定作為函數,方法,構造器的形參、傳回值類型

作為泛型參數

作為is、as、as?、as!等運算符的後一個操作數

protocol Eatable {
    func taste()
    static func test(msg:String...)
}
//結構體實作協定
struct Pie:Eatable{
    var weight:Double
    func taste() {
        print("\(weight) 重量的餅幹才能吃飽")
    }
    
    static func test(msg: String...) {
        print("實作關于餅幹的類方法")
        for mymsg in msg{
            print("here is the para \(mymsg)")
        }
    }
    
}
//類實作協定
class Apple: Eatable {
    
    
    var name:String
    init(name:String) {
        self.name=name
    }
    func taste() {
        print("\(name) is full of water")
    }
    static func test(msg: String...) {
        for mymsg in msg{
            print("個數可變的形參\(mymsg)")
        }
    }
}
//将Apple和Pie的執行個體指派給food,這是向上轉型
var food1:Eatable=Apple(name: "紅富士")
var food2:Eatable=Pie(weight: 1.2)
food1.taste()
food2.taste()
//協定執行個體作為形參
func eat(foods:Eatable...){
    for food in foods{
        food.taste()
    }
}
//即可将Apple類執行個體也可将Pie類執行個體傳入作為形參,應為形參要求是Eatable類型,這兩者都是Eatable協定的實作類
eat(foods: Apple(name: "紅富士"),Pie(weight: 2.3))

//使用Eatable協定類型作為數組類型
var foodArray:[Eatable] = [Apple(name: "紅富士"),Apple(name: "冰糖心"),Pie(weight: 2.3),Pie(weight: 3.4)]
for food in foodArray{
    if let ap=food as?Apple{
        print(ap.name)
    }else if let pie=food as?Pie{
        print(pie.weight)
    }
}
           

列印結果:

紅富士 is full of water

1.2 重量的餅幹才能吃飽

紅富士 is full of water

2.3 重量的餅幹才能吃飽

紅富士

冰糖心

2.3

3.4

9.合成協定(多個協定合在一起)

一般性格式

protocol (協定1 &協定2 &協定3 &…)//swift4.0中已經是用&符号替代協定之間的逗号(,)

protocol Strokable {
    var strokeWidth:Double{get set}
}

enum Color{
    case Red,Green,Blue,Yellow,Cyan
}
protocol Fullable {
    var fullColor:Color?{get set}
}

protocol HasArea:Fullable,Strokable {
    var area:Double {get}
}
protocol Mathable {
    static var pi:Double{get}
    static var e:Double{get}
}
struct Rect:HasArea,Mathable{
    var fullColor: Color?
    var strokeWidth: Double=0.0
    //+++++++++++++++++++
    
    var area: Double{
        get{ return width*height}
    }
    //+++++++++++++++++++
    
    static var pi: Double=3.1415926
    static var e: Double=2.718
    //+++++++++++++++++++
    var width:Double
    var height:Double
    init(width:Double,height:Double) {
        self.width=width
        self.height=height
    }
    
}

class Circle:HasArea,Mathable{
    var radius:Double
    init(radius:Double) {
        self.radius=radius
    }
    var fullColor: Color?
    var strokeWidth: Double=0.0
    var area: Double{
        get{
            return Circle.pi*radius*radius//實作協定是,這個變量被繼承下來,接下來也将多這個屬性指派,是以應該使用Circle.pi而不是使用Mathable.pi。如果使用Mathable.pi則會報錯誤:Static member 'pi' cannot be used on protocol metatype 'Mathable.Protocol'
        }
    }
    static var pi:Double=3.1415926
    static var e:Double=2.714
}

//合成協定
func test(arg:HasArea & Mathable){
    print("arg參數的填充顔色是\(arg.fullColor)")
    print("arg參數描邊的粗細是\(arg.strokeWidth)")
    print("arg參數的面積是\(arg.area)")
}

var circle=Circle(radius:1.2)
circle.strokeWidth=0.5
circle.fullColor=Color.Red

var rect=Rect(width: 3.4, height: 2.8)
rect.strokeWidth=0.8
rect.fullColor=Color.Green
//test函數必須同時實作HasArea、Mathable協定,是以可以傳入Circle,Rect執行個體
test(arg: circle)
test(arg: rect)
           

列印結果

arg參數的填充顔色是Optional(MyEX011.Color.Red)

arg參數描邊的粗細是0.5

arg參數的面積是4.523893343999999

arg參數的填充顔色是Optional(MyEX011.Color.Green)

arg參數描邊的粗細是0.8

arg參數的面積是9.52

10.通過擴充為已有的類型添加協定
protocol Eatable {
    func taste()
}
//通過擴充讓String實作Eatable
extension String:Eatable{
    func taste(){
        print("\(self)吃起來味道不錯")
    }
}
func eat(foods:Eatable...){
    for food in foods {
        food.taste()
    }
}

eat(foods: "花生","瓜子","八寶粥")
           

列印結果

花生吃起來味道不錯

瓜子吃起來味道不錯

八寶粥吃起來味道不錯

如果已有類型敲好實作了協定的要求,則可以更簡單地通過擴充讓該類型實作這個協定,此時擴充的花括号内不需要書寫任何代碼

protocol Emptytable{
    var isEmpty:Bool{get}
}
//通過擴充讓String補充實作Emptytable協定
//由于String已經實作了Emptable協定的方法,是以擴充中無須任何代碼
extension String:Emptytable{}
//定義一個方法,該方法需要EMptable參數
func foo(arg:Emptytable){
    print("arg是否為空:\(arg.isEmpty)")
}
foo(arg: "hello world")
foo(arg: "")
           
11.唯類(class-only)協定(隻能被類實作的協定)

一般格式:

protocal 協定名:class,父協定1,父協定2,…{

//協定的定義

}

protocol Movable:class{//定義一個唯類協定
    func move()
}
class Car:Movable{
    func move() {
        print("we are moving")
    }
}
var car=Car()
car.move()
           

列印結果

we are moving

12.可選協定

為了和oc協定相容,可選協定簽必須添加@objc修飾符并且在協定成員前添加optional關鍵字即可定義可選協定

swift4要求每個協定成員之前都要加@objc

@objc protocol MyProtocol{
    @objc optional var status:String{get}
    @objc optional func increment(val:Int)
    @objc optional subscript(idx:Int)->Int{get}
}
//由于是可選的協定,是以可以完全不實作,定義一個空類
class EmptyClass:NSObject,MyProtocol{

}
@objc class Myclass:NSObject,MyProtocol{
    var name:String
    init(name:String){
        self.name=name
    }
    //實作可選協定的一個屬性
    var status: String{
        if name.utf16.count<10{
            return "良好"
        }else{
            return "超長"
        }
    }
    //實作可選協定的方法
    func increment(val: Int) {
        print("系統正在增長長度")
        for _ in 1...val{
            name+="="
        }
    }
}

var mp:MyProtocol=Myclass(name: "張三豐")
print(mp.status)
print(mp[4])
mp.increment?(val: 10)//這裡是用了?可選鍊
print(mp.status)

var ec:MyProtocol=EmptyClass()
print(ec.status)
print(ec[4])
ec.increment?(val: 10)//這裡是用了?可選鍊
           
12.協定擴充
protocol Eatable {
     func taste()
    static func test(msgs:String...)
}
//擴充協定,在擴充部分,甚至提供了部分的實作
extension Eatable{
    var type:String{
        return "食物"
    }
    func info(){
        print("至少可以吃")
    }
    subscript (idx:Int)->Int{
        return idx*idx
    }
    /*可以在擴充裡面直接實作接口的類方法,提供預設的實作
    static func test(msgs: String...) {
        print("通過擴充實作的預設test方法")
        for msg in msgs{
            print("個數可變的形參:\(msg)")
        }
    }
    */
}
//類實作Eatable協定
class Apple:Eatable{
    var name:String
    init(name:String){
        self.name=name
    }
    //實作協定中執行個體方法
    func taste(){
        print("\(name)水分充足,營養豐富")
    }
    //實作協定中的類方法,使用class修飾符修飾
    class func test(msgs: String...) {
        print("蘋果實作的test方法")
        for msg in msgs{
            print("個數可變的形參:\(msg)")
        }
    }
}


//結構體實作Eatable協定
struct Pie:Eatable {
    var weight:Double
    init(weight:Double){
        self.weight=weight
    }
    
    static func test(msgs: String...) {
        print("蘋果實作的test方法")
        for msg in msgs{
            print("個數可變的形參:\(msg)")
        }
    }
    
    func taste(){
        print("\(weight)公斤餅幹剛剛能吃飽")
    }

}

let pie:Pie=Pie(weight: 2.3)
pie.taste()
print(pie.type)//擴充來的屬性
pie.info()//擴充來的方法
print(pie[3])
Apple.test(msgs: "紅富士","冰糖心")
Pie.test(msgs:"吉百利","餡餅")
           

列印結果:

2.3公斤餅幹剛剛能吃飽

食物

至少可以吃

9

蘋果實作的test方法

個數可變的形參:紅富士

個數可變的形參:冰糖心

蘋果實作的test方法

個數可變的形參:吉百利

個數可變的形參:餡餅

12.輸出執行個體和CustomStringConvertible

如果希望輸出執行個體時,能看到實物的内部狀态,可以讓該類實作CustomStringConvertible類協定,并實作該協定中的description隻讀屬性

class Person: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)"
        
    }
}

var p1=Person(name: "張三豐", age: 100)
print(p1)
print(p1.description)
           

列印結果

name=張三豐—100

name=張三豐—100

13.使用自定義類型作為字典的key(==運算符重載)(實作Equatable協定)

字典的兩個key相等,需要滿足

1、兩個key通過= =比較傳回true

2、兩個key的hashvalue屬性傳回相等的整數

自定義類型執行個體的比較需要

1、讓自定義類型實作Equatbale協定,并重載“= =”比較運算符,使得自定義類型的執行個體可以通過“= =”進行比較

2、讓自定義類型實作Hashable協定,并實作該協定的hashValue隻讀屬性。實作Hashable隻讀屬性時,應和重載的==保持一緻。

class User:Equatable{
    var name:String
    var password:String
    var age:Int
    init(name:String,password:String,age:Int){
        self.name=name
        self.password=password
        self.age=age
    }
}
//重載= =運算符
//(實際上就是重新定義名為= =的函數,但形參清單與已有的= =函數形參清單并不相同)
func = = (lhs:User,rhs:User)->Bool{
    //當兩個User執行個體的name,password都相等時,即認為兩個User執行個體相等
    return lhs.name==rhs.name && lhs.password == lhs.password
}
var u1=User(name: "zhangsanfeng", password: "123456", age: 23)
var u2=User(name: "zhangsanfeng", password: "123456", age: 34)
print(u1==u2)
var u3=User(name: "luoyulong", password: "123456", age: 34)
print(u1==u3)
print(u2 != u3)
           

列印結果

true

false

true

struct User:Hashable,CustomStringConvertible {
    var name:String
    var password:String
    var age:Int
    init(name:String,password:String,age:Int){
        self.name=name
        self.password=password
        self.age=age
    }
    var hashValue:Int{
        return name.hashValue &* 31 &+ password.hashValue//考慮到資料可能溢出,故此處采用溢出運算符
    }
    var description: String{
        return "\(name)--\(password)--\(age)"
    }
}
//重載= =運算符
func == (lhs:User,rhs:User)->Bool{
    return lhs.name==rhs.name && lhs.password==lhs.password
}

var dict=[User:Int]()
dict[User(name: "zhangsanfeng", password: "123", age: 100)]=50
dict[User(name: "yuebuqu", password: "133", age: 34)]=220
dict[User(name: "zhangsanfeng", password: "123", age: 43)]=100
print(dict)
           

列印結果

[zhangsanfeng–123–43: 100, yuebuqu–133–34: 220]

繼續閱讀