swift之Protocol

协议(Protocol)

swift中协议用于定义多个类型应该遵守的规范。协议定义规范,类是协议的一种具体实现,协议不关心类内部的状态数据和实现细节,它只规定这批类里面必须提供某些方法,提供这些方法的类就能满足某种需要。协议定义了多个类型的共同的公共行为规范,这些欣慰是与外部交流的通道,协议同意了方法名、属性名和下标,但是协议不提供任何实现。实现协议的枚举、结构体、类成为协议的遵守者,协议实现者必须提供协议要求的属性、方法、构造器、下标等。

协议本身

语法

protocol SomeProtocol: SuperProtocol1, SuperProtocol2, ... {
    //protocol defination goes here
    //property,method,initializer,subscripts
}

struct SomeStructure: SomeProtocol {
    //structure defination goes here
}
class SomeClass: SomeProtocol {
    //class defination goes here
}

enum SomeEnumeration: SomeProtocol {
    //enumeration defination goes here
}

协议指定的属性要求(Property Requirements)

协议可以要求实现者必须提供包含特定名称的实例属性或者类型属性,也能要求该属性是否有get部分和set部分,但是不关心该属性是存储属性还是实例属性

如果协议要求属性是可读可写的,那么遵循协议的类型 中的相同名称的属性必须是var修饰的,如果协议要求属性是可读的,那么遵循协议的类型中的属性用var或者let修饰都是可以的。

protocol PropertyRequirementExample {
    //一般属性
    var mustBeSettable : Int { get set }
    var doesNotNeedToBeSettable : Int { get }
    //类型属性
    static var someTypeProperty : Int { get set}
}

以下是一个具体例子:

protocol FullyNamed {
    var fullName : String {get}
}

struct Person : FullyNamed {
    //computed property ---> stored property
    var fullName: String
    init(_ name : String) {
        fullName = name
    }
}
class Starship: FullyNamed {
    var prefix : String?
    var name : String
    init(_ name : String, _ prefix : String? = nil) {
        self.name = name
        self.prefix = prefix
    }
    var fullName: String{
        return (nil == prefix ? "" : prefix! + " ") + name
    }
}

协议指定方法要求(Method Requirements)

协议要求实现的方法可以是实例方法,类方法,以及mutating修饰的方法,但不能是默认参数的方法

protocol RandomNumberGenerator {
    func random() -> Double
}
class LinearCongruentiaGenerator: RandomNumberGenerator {
    var lastRandom = 42.0
    let m = 139968.0
    let a = 3877.0
    let c = 29573.0
    func random() -> Double {
        lastRandom = ((lastRandom * a + c)).truncatingRemainder(dividingBy: m)
        return lastRandom / m
    }
}
let generator = LinearCongruentiaGenerator()
print("Random number is : \(generator.random())")
print("Other random number is : \(generator.random())")

协议指定的可变方法的要求(Mutating Method Requirements)

枚举和结构体需要定义能改变实例数据的方法,则需要将该方法声明为可变方法(Mutating修饰)。协议中也可以定义可变方法,然后在枚举和结构体实现时需要声明为可变方法(Mutating修饰),类中实现则不需要Mutating修饰。

protocol Togglable {
    mutating func toggle()
}
enum Switch : Togglable {
    case off, on
    mutating func toggle() {
        switch self {
        case .on:
            self = .off
        case .off:
            self = .on
        }
    }
}

协议指定的下标的要求

协议可以要求实现者必须提供哪些下标,也能要求该下标是否有set和get部分,其中set部分可选。

注意:如果协议指定下标是只读的,只有get部分,实现的时候也可以提供表的set部分。即协议只要求必须实现的,至于额外提供什么,协议不做过多限制。

protocol Mathable {
    //要求get方法
    subscript(idx: Int) -> Int { get }
    subscript(a: Int, b: Int) -> Int { get }
}

struct LinearStruct: Mathable {
    var factor: Int
    //提供了get和set方法
    subscript(idx: Int) -> Int {
        get {
            return factor * idx
        }
        set {
            print("Set value to LinearStruct by subsript!")
        }
    }

    //只提供了get方法
    subscript(a: Int, b: Int) -> Int {
        return factor * a + b
    }
}

class Quadratic: Mathable {
    subscript(idx: Int) -> Int {
        return factor * factor * idx
    }

    subscript(a: Int, b: Int) -> Int {
        return factor * factor * a + b
    }

    var factor: Int
    init(factor: Int) {
        self.factor = factor
    }
}

var linear = LinearStruct(factor: 2)
print("\(linear[5]),   \(linear[3,7])")
var quadratic = Quadratic(factor: 2)
print("\(quadratic[4]),    \(quadratic[4,5])")

协议指定的构造器要求(Initailizer Requirements)

协议可以要求实现者必须实现那些构造器。枚举和结构体实现协议中的构造器比较简单,不需要特别的注意。当使用类来实现协议中的构造器时,可以使用指定构造器也可以使用便利构造器实现,协议不做明显的限制。但是需要注意:

  1. 类实现协议并且实现协议中那个的构造器时,必须用required修饰,除非该类被final修饰,不能被继承。
  2. 类实现协议并且实现协议中那个的构造器时,如果该构造器还同时重写了父类的构造器,则必须用rquired和override修饰。
protocol TempProtocol {
    init()
}

class SuperClass {
    init() {
    }
}

class SomeSubClass : SuperClass,TempProtocol {
    required override init() {
    }
}

使用协议作为类型(Protocol as Types)

协议也相当于一种类型,与枚举、结构体和类相比,协议相当于一种抽象类型,因为它只定义了规范而没有负责实现。可以像使用枚举、结构体和类一样使用协议,只是不能用来创建实例

  1. 可以声明变量
  2. 可以使用协议作为方法、函数、构造器的形参和返回值
  3. 可以用作泛型参数---指定数组字典元素类型
  4. 可以使用is、as、as?、as!操作符。
protocol RandomNumberGenerator {
    func random() -> Double
}

class Dice {
    let sides : Int
    let generator : RandomNumberGenerator
    init(sides: Int, generator:RandomNumberGenerator) {
        self.sides = sides
        self.generator = generator
    }
    func roll() -> Int {
        return Int(generator.random() * Double(sides)) + 1
    }
}

协议继承

协议可以继承一个或者多个其他协议,语法与类继承语法类似。

 protocol SomeProtocol {}
 protocol AnotherProtocol {}
 protocol SubProtocol: SomeProtocol,  AnotherProtocol {}

类专用协议

如果协议继承列表添加AnyObject,那么这个协议只能被类所继承。如果 值类型强制执行会报错:

Non-class type 'SomeStruct' cannot conform to class protocol 'SomeProtocol'
 protocol SomeProtocol: AnyObject{}
 class  SomeClass: SomeProtocol {}

协议组合

协议组合用于复合多个协议到一个要求里,协议组合类似于局部临时协议,其拥有构成中的所有协议的要求,但是不定义任何新的协议要求。协议组合也能包含类类型,允许表明一个需要的父类

 protocol  Named {
    var name: String {get}
 }
 protocol Aged {
    var age: Int {get}
 }

 struct Person: Named,Aged {
    var name: String
    var age: Int
 }

 struct Pet: Named,Aged {
    var name: String
    var age: Int
 }
 func wishHappyBirthday(to celebrator: Aged & Named)  {
    print("Happy  birthday, \(celebrator.name), you are \(celebrator.age).")
 }
let robin = Person(name: "Robin", age: 20)
wishHappyBirthday(to: robin)

协议和扩展

扩展现有类型使其遵循协议

可以通过扩展是一个已存在类型遵循使其遵循新的协议,即便你没有办法访问类型的源代码。这个功能依赖于:扩展可以给已存在类型添加新的属性、下标和方法,这满足协议的任何需求。

 protocol TextRepresentable  {
    var  textDescription: String  {get}
 }

 extension Int: TextRepresentable {
    var textDescription: String {
        return "\(self)"
    }
 }

有条件遵循协议

 protocol TextRepresentable  {
    var  textDescription: String  {get}
 }

 extension Int: TextRepresentable {
    var textDescription: String {
        return "\(self)"
    }
 }

 extension Array: TextRepresentable where Element: TextRepresentable {
    var textDescription: String {
        let itemsAsText = self.map{$0.textDescription}
        return "[" + itemsAsText.joined(separator: " ") + "]"
    }
 }
 let arr = [1,2,3,4,5]
 print(arr.textDescription)

使用扩展声明采纳协议

如果已存在类型已经遵循了协议的所有需求,而这个类型没有声明采纳协议,可以通过空的扩展让他采纳这个协议。

 protocol  Named {
    var name: String {get}
 }
 protocol Aged {
    var age: Int {get}
 }

 struct Person {
    var name: String
    var age: Int
 }

 extension Person: Named & Aged  {}

协议的扩展

1.可以通过扩展给协议添加默认实现。

 protocol  Named {
    var name: String {get}
 }

 extension Named {
    var name: String {
        return  "Tom"
    }
 }

 struct PersonWithDefaultName: Named  {}
 struct PersonWithSpecificName: Named {
    var first: String
    var last: String
    var name: String {
        return  "\(first) \(last)"
    }
 }

2.通过扩展给协议增加需要提供的方法和属性,并提供默认实现。

 protocol RandomNumberGenerator {
     func random() -> Double
 }
 extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random()  > 0.5
    }
 }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349