协议(Protocols)

    协议定义了某个特殊的任务或者某个功能片段的蓝图,包括了方法,属性,以及其他的需求。协议可以被类,结构体和枚举遵循,并且为这些需求提供真正的实现。满足协议中这些需求的任何类型被称为遵循了这个协议。

    除了让遵循协议的类型实现需求,你也可以对协议进行扩展,并且实现这些需求,或者实现一些其他功能,让遵循了协议的类可以使用。

协议的语法

    定义协议和定义类,结构体以及枚举的方式很像:

            protocol SomeProtocol {

                // protocol definition goes here

        }

    自定义的类型通过把协议的名字放在类型后面,通过冒号分隔来遵循协议,这是定义的一部分。多个协议可以被列在后面,用逗号分隔:

            struct SomeStructure: FirstProtocol, AnotherProtocol {

            // structure definition goes here

        }

    如果类有父类,那么把父类的名字放在协议的名字前面,用逗号分隔:

        class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {

            // class definition goes here

        }

属性需求

    协议可以要求任何遵循的类根据指定的名字和类型提供一个实例属性或者类型属性。协议不会指定属性应该是存储属性还是计算属性而是仅仅指定属性的名字和类型。协议也可以指定每一个属性是可读还是可读写的。

    如果协议要求一个属性是可读写的,那么一个存储的常量属性或者一个只读的计算属性将无法满足属性的要求。如果属性要求一个属性是可读的,那么任何属性都可以满足要求,就算属性是可写的也是有效的,只要这样子对你的代码是有用的。

    协议中属性的需求总是被声明为变量属性,用var关键字作为前缀。可读写的属性通过在声明之后用写上{ get set }注明,可读的属性通过写上{ get }注明。

        protocol SomeProtocol {

            var mustBeSettable: Int { get set }

            var doesNotNeedToBeSettable: Int { get }

        }

    在协议中定义类属性时,总是使用static关键字前缀,即使在类实现这个类属性的时候需要使用class或者static关键字,这个规则也同样适用:

        protocol AnotherProtocol {

            static var someTypeProperty: Int { get set }

        }

    下面是一个例子,一个简单的协议,有一个实例属性的要求:

        protocol FullyNamed {

            var fullName: String { get }

        }

    协议FullyNamed需要一个类遵循,并且提供一个全名。协议不在乎遵循的类型是什么,只是要求这个遵循的类型必要提供一个和他相关的全名。上述协议就要求所有遵循FullyNamed的类型必须提供一个可读的实例属性,名字叫做FullyNamed,类型是String。

    如下例,一个遵循了协议FullyNamed的结构体:

        struct Person: FullyNamed {

            var fullName: String

        }

        let john = Person(fullName: "John Appleseed")

        // john.fullName is "John Appleseed

    例子中定义了一个叫做Person的结构体,可以表示出一个人的名字。他在定义的第一行声明了自己遵循了FullyNamed协议。

    每一个Person的实例都有一个存储属性,叫做fullName,类型是String,这样子已经满足了FullyNamed协议,也就是说Person争取的遵循了FullyNamed协议。当然,当协议没有被完全满足的情况下,Swift会有一个编译时错误。

    下面说一下更加复杂的例子,它们都遵循了FullyNamed协议:

        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 (prefix != nil ? prefix! + " " : "") + name

            }

        }

        var ncc1701 = Starship(name: "Enterprise", prefix: "USS")

        // ncc1701.fullName is "USS Enterprise

这个类实现了fullName属性的需求,它是一个StartShip的可读的计算属性。每一个StartShip类型的实例都肯定有一个name,还可能有prefix。fullNameprefixname相连作为fullName返回,如火不存在prefix那么值返回name

方法需求

    协议可以要求遵循的给实现是定的实例方法和类方法。这些方法和普通的实例方法和类方法写在协议定义的部分,不需要花括号和具体的实现。

    和属性需求一样,在协议中定义类方法的时候需要使用static关键字。即使在类实现的时候需要使用classstatic前缀的时候也成立。

        protocol SomeProtocol {

            static func someTypeMethod()

        }

    下面是一个定义了一个实例方法的协议的例子:

        protocol RandomNumberGenerator {

            func random() -> Double

        }

    协议RandomNumberGenerator希望所有遵循的类都实现实例方法random,并且返回一个Double值。我们假设这个返回值是从0.0到1.0的值,尽管协议里面并没指定。

    协议RandomNumberGenerator并没有给出对于随机数产生的猜想,只是简单地表示需要一个简单的随机数生成器,这样就可以使用标准的方法获取随机数。

    下面是一个遵循了RandomNumberGenerator协议的实现类。这个类实现了一个伪随机的数字生成器,也就是线性同余生成器:

        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).truncatingRemainder(dividingBy:m))

                return lastRandom / m

            }

        }

        let generator = LinearCongruentialGenerator()

        print("Here's a random number: \(generator.random())")

        // 打印 "Here's a random number: 0.37464991998171"

        print("And another one: \(generator.random())")

        // 打印 "And another one: 0.729023776863283”

可变方法的需求

    有一些时候,一个方法需求需要改变当前实例对象,对于值类型的实例,通过在方        法前加上mutating关键字,表示方法允许修改当前实例以及实例的任何属性。

    如果你在协议中定义了一个需要改变遵循类实例的实例方法,那么请在方法前加上mutating关键字。这样就可以让枚举和结构体遵循协议并且满足协议。

NOTE:mutating关键字是适用于结构体和枚举。也就是说,一个协议中有一个带有mutatin关键字的实例方法,使用类去遵循并实现该方法的时候,不需要添加mutatiing关键字。

    下面的自己定义了一个叫做Togglable的协议,并有一个实例方法叫做toggle。正如方法的名字所示,方法toggle要改变任何遵循的类型的状态。通常要改变类型的属性。

    在协议Togglable的定义中方法toggle被标记了mutating关键字,表示这个方法在被调用的希望可以改变遵循的类型的实例。

        protocol Togglable {

            mutating func toggle()

        }

    如果你为了结构体和枚举实现了协议Togglable,那么就需要提供一个同样标记为mutating的方法toggle

        enum OnOffSwitch: Togglable {

            case off, on

            mutating func toggle() {

                switch self {

                case .off:

                    self = .on

                case .on:

                    self = .off

                }

            }

        }

        var lightSwitch = OnOffSwitch.off

        lightSwitch.toggle()

        // lightSwitch is now equal to .on

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

推荐阅读更多精彩内容