Swift -Protocol

// 本文内容来自慕课网----玩儿转Swiftswift

1 协议中能够定义属性app

  (1)属性不能够有默认值iphone

  (2)必须设置是“get”仍是“get set”,注意:get与set之间是没有逗号的函数

  (3)即便属性只有get,没有set,也不能够将属性设置为let,必须设置为varui

2 协议中能够定义方法spa

  (1)方法的参数不能够有默认值设计

复制代码
protocol Pet {
    
    // 定义属性
    var name: String {get set}
    var birthplace: String {get}    

    // 定义方法
    func fed(food: String)
    func playWith()
   mutating func changeName(name: String) }
 
复制代码
3 如下写法中,表示pet遵照Pet协议。因为Pet不是类,故不能用Pet()来对pet进行初始化。
var pet: Pet 

4 定义一个结构体实现该协议code

  (1)实现协议中的属性orm

    (I)此时属性能够设置默认值对象

    (II)协议中name为可读可写的,能够直接声明为var类型就能够

    (III)协议中birthplace为可读属性,能够直接声明为let类型就能够,固然也能够声明为var类型

  (2)实现协议中的方法

    (I)能够为方法中的参数设置默认值

    (II)在结构体中,若是须要改变自身的值,须要在方法前面加mutating关键字。在协议的方法中添加mutating关键字,若是结构体来遵照协议,须要有  mutating这个关键字,若是是类来遵照协议,mutating关键字就不须要了。

复制代码
struct Dog: Pet {

    var name: String = "Tim"
    let birthplace: String = "Bei Jing"
    
    func fed(food: String = "Bone") {
        
        if food == "Bone" {
            print("I am happy.")
        } else {
            print("I need a bone.")
        }
    }
    
    func playWith() {
         print("Wong!")
    }
    
    mutating func changeName(name: String) {
        self.name = name
    }
}
复制代码

 5 将一个Dog类赋值给一个遵照了Pet协议的对象,是没有问题的。由于协议能够看成一个类型来看待。

var dog: Dog = Dog()
let aPet: Pet = dog

6 若是只但愿协议只被类class遵照,只须要在定义协议的时候在后面加上AnyObject便可。

 如下定义的Animate协议只能被类遵照,结构体是不能够遵照的。

复制代码
protocol Animate: AnyObject {
    var name: String {get set}
}

class Cat: Animate {
    
    var name: String = ""
}
复制代码

7 Dog类在实现协议Pet的时候,若是将birthplace声明为var是没有问题的。若是birthplace被看成是Dog的属性,它是能够赋值的,但若是birthplace被做为是Pet的属性,它是不能够赋值的。

复制代码
struct Dog: Pet {
    // 其他的属性和方法省略
    var birthplace: String = "Beijing"
}

var dog: Dog = Dog()
let aPet: Pet = dog

// 对dog的birthplace属性赋值是没有问题的 dog.birthplace = "Shanghai"
// 不能够对aPet的birthplace属性赋值。由于在协议Pet中,birthplace是只读的
// aPet.birthplace = "Hangzhou"
复制代码

 8 若是协议(Protocol Pet)中定义了构造函数(init),则实现协议的类(Dog)必须实现这个构造函数,而继承该类(Dog)的子类(Taidi)间接的也实现了该协议,故子类(Taidi)也必须实现这个构造函数。所以须要在类(Dog)的构造函数中填加子类(Taidi)必须实现该方法的标识,即关键字required。若是类(Dog)定义为final类,即其它类不能够继承该类,则required关键字能够省略。

复制代码
protocol Pet {
    
    var name: String {get set}
    var birthplace: String {get}
    
    // 定义构造函数
    init(name: String)
}

class Animate {
    
    var type = "mamal"
}

class Dog: Animate, Pet {

    var name: String = "Tim"
    var birthplace: String = "Bei Jing"
    
    required init(name: String) {
        self.name = name
    }
}

// Dog类也能够这样写
final class Dog: Animate, Pet {
    
    var name: String = "Tim"
    var birthplace: String = "Bei Jing"
    // 此时required关键字就能够省略
    init(name: String) {
        self.name = name
    }
}
复制代码

 9 类型别名(typealias)和关联类型(associatedtype)

  类型别名(typealias)其实就是给类型从新定义一个名字。  

复制代码
extension Double {    
    var km: Double {return self * 1000.0}
    var m: Double {return self}
    var cm: Double {return self / 100}
    var ft: Double {return self / 3.28084}
}
let runningDistance: Double = 3.54.km

// 咱们能够给Double设置一个别名Length,则以上代码就能够改成下面的代码
typealias Length = Double
extension Double {
    var km: Length {return self * 1000.0}
    var m: Length {return self}
    var cm: Length {return self / 100}
    var ft: Length {return self / 3.28084}
}
let runningDistance: Length = 3.54.km
复制代码

  在设计协议(protocol)时,若是有两个协议,它们的方法和属性都同样,只有协议中用到的类型不一样,咱们就不必定义两个协议,只须要将这两个协议合并为一个就能够了。这时就能够在协议中使用关联类型(associatedtype),相似于类型别名(typealias)。

复制代码
protocol WeightCalaulate {

    associatedtype weightType
    
    var weight: weightType {get}
}
// 在类iphone7中,weightType为Double类型的别名
class iphone7: WeightCalaulate {

    typealias weightType = Double
    
    var weight: weightType {
        return 0.114
    }
}
// 在类Ship中,weightType为Int类型的别名
class Ship: WeightCalaulate {

    typealias weightType = Int
    
    var weight: weightType {
        return 46_328_000
    }
}
复制代码

10 协议能够继承、能够扩展

  (1)先看下面的代码:定义了一个协议Record,两个结构体BaseballRecord、BasketballRecord,这两个结构体都实现了Record和CustomStringConvertible协议。

复制代码
protocol Record {
    var wins: Int {get}
    var loses: Int {get}
    func winningPercent() -> Double
}

struct BaseballRecord: Record, CustomStringConvertible {
    var wins: Int
    var loses: Int
    func winningPercent() -> Double {
        return Double(wins)/Double(wins + loses)
    }
    
    var description: String {
        return String(format: "WINS: %d, LOSES: %d", wins, loses)
    }
}

struct BasketballRecord: Record, CustomStringConvertible {  
    var wins: Int
    var loses: Int
    func winningPercent() -> Double {
        return Double(wins)/Double(wins + loses)
    }
var description: String { return String(format: "WINS: %d, LOSES: %d", wins, loses) } }
复制代码

  (2)能够看到,以上两个结构体都实现了Record, CustomStringConvertible两个协议,若是咱们但愿只要实现Record协议,就须要实现CustomStringConvertible协议。咱们可让Record协议继承自CustomStringConvertible协议,这样只要实现Record协议,就必须实现CustomStringConvertible协议。代码以下:

复制代码
// 协议中定义代码及结构体中实现的代码同上,此处省略
protocol Record: CustomStringConvertible { } struct BaseballRecord: Record { } struct BasketballRecord: Record { }
复制代码

  (3)能够看到,两个结构体中实现协议的代码是相同的。若是这一部分代码能够写到协议中,在结构体中就能够省去重复的代码。又协议的定义(Protocol Record)中是不能够实现代码的,咱们能够经过扩展协议的方式,在扩展中实现相应的代码。(在扩展中能够进行一些默认的实现)

复制代码
protocol Record: CustomStringConvertible {
    var wins: Int {get}
    var loses: Int {get}
    func winningPercent() -> Double
}

extension Record {
  // 定义一个属性   
    var gamePlayed: Int {
        return wins + loses
    }
    // 实现Record协议中定义的方法
    func winningPercent() -> Double {
        return Double(wins)/Double(gamePlayed)
    }
    // 实现CustomStringConvertible协议
    var description: String {
        return String(format: "WINS: %d, LOSES: %d", wins, loses)
    }
} struct BaseballRecord: Record { var wins: Int var loses: Int } struct BasketballRecord: Record { var wins: Int var loses: Int } let baseball = BaseballRecord(wins: 3, loses: 2) baseball.winningPercent() print(baseball) // WINS: 3, LOSES: 2
复制代码

  (4)能够扩展系统中的协议

复制代码
extension CustomStringConvertible {
    var descriptionWithDate: String {
        return NSDate().description + description
    }
}

baseball.descriptionWithDate
复制代码

  (5)在协议的扩展中定义的属性,在实现该协议的结构体中仍能够重写该属性。 

struct BaseballRecord: Record {
    var wins: Int
    var loses: Int
    // 重写了协议扩展中定义的属性gamePlayed
    let gamePlayed: Int = 162
}

  (6)用where关键字对协议作条件限定(where 类型限定)

    (I)第一一个结构体FootballRecord,它实现Record协议,而且它新增了一个属性(平局ties),这样它的gamePlayed属性以及winningPercent()方法都须要重写。代码以下:

复制代码
struct FootballRecord: Record {
    var wins: Int
    var loses: Int
    // 定义平局的属性
    var ties: Int
    
    var gamePlayed: Int {
        return wins + loses + ties
    }
    
    func winningPercent() -> Double {
        return Double(wins)/Double(gamePlayed)
    }
}

let football = FootballRecord(wins: 1, loses: 1, ties: 1)
football.gamePlayed
football.winningPercent()
复制代码

    (II)若是按上面这样写,那么具备“平局属性”的那些结构体都须要写上面的那些所有代码。故咱们能够再写一个协议Tieable,而实现了该协议Tieable的结构体的gamePlayed属性及winningPercent()方法都应该从新定义。能够对Record协议进行扩展,而且在扩展中增长限制,只有实现了Tieable协议的那些结构体才能够有这个扩展中的方法和属性。

复制代码
// 既实现了Record协议,又实现了Tieable协议的结构体(类),才可使用这个扩展中的属性、方法
extension Record where Self: Tieable {
    
    var gamePlayed: Int {
        return wins + loses + ties
    }
    
    func winningPercent() -> Double {
        return Double(wins)/Double(gamePlayed)
    }
}
struct FootballRecord: Record, Tieable {

    var wins: Int
    var loses: Int

    var ties: Int
}

let football = FootballRecord(wins: 1, loses: 1, ties: 1)
football.gamePlayed      // 3
football.winningPercent()  // 0.3333333333
复制代码

   (7)协议聚合 

复制代码
protocol Prizable {    
    func isPrizable() -> Bool
}

// 表示Prizable和CustomStringConvertible两个协议都实现了的结构体才能够调用该方法
func award(one: Prizable & CustomStringConvertible) {
    
}
复制代码

  (8)泛型约束(在方法定义中能够用T来表明某个协议,只须要用<T: 协议名>来定义协议就好)

func top<T: Record>(seq: [T]) -> T {    
}
// 多个协议
func top<T: Record & Prizable>(seq: [T]) -> T {
}

  (9)可选协议:如下协议被标识为@objc属性,使得它兼容Objective-C代码。若是协议拥有可选的属性或方法时,是必须添加@objc的,由于Swift要使用Objective-C的运行时来检查类所遵照的可选方法是否存在。拥有可选方法的协议只能被类遵照,结构体和枚举是不能够遵照该协议的。

@objc protocol Animal {  // 注意: 在swift3中optional前面也必须有@objc
    @objc optional func fly()
}