Swift中的Protocol知道这些就够了

什么是Protocol?


Protocol是Swift中的一种自定义类型,可以使用protocol定义某种约定,而不是某一种类型,一般用于表示某种类型的共性。

Protocol 用法


定义一个protocol
protocol PersonProtocol {
    func getName()
    func getSex()
}
某个class、struct或者enum要遵守这种约定的话,需要实现约定的方法
struct Person: PersonProtocol {
    func getName() {
         print("MelodyZhy")
    }
    func getSex() {
         print("boy")
    }
}
protocol中的约定方法,当方法中有参数时是不能有默认值的
protocol中也可以定义属性,但必须明确指定该属性支持的操作:只读(get)或者是可读写(get set)
protocol PersonProtocol {
    // 我们也可以在protocol中定义属性
    // ⚠️必须明确指定该属性支持的操作:只读(get)或者是可读写(get set)
    var height: Int { get set }
    func getName()
    func getSex()
    // protocol中的约定方法,当方法中有参数时是不能有默认值的
    // ❌ Default argument not permitted in a protocol method
    // func getAge(age: Int = 18)
    func getAge(age: Int)
}
虽然height在protocol中是一个computed property,但在遵守该约定的类型中可以简单的定义成一个stored property
当protocol中定义了一个只读属性,其实我们也可以在遵守该约定的类型中完成该属性的可读可写
protocol PersonProtocol {
    var height: Int { get set }
    var weight: Int { get }
    func getName()
    func getSex()
    func getAge(age: Int)
}

struct Person: PersonProtocol {
    var height = 178
    var weight = 120
    func getName() {
         print("MelodyZhy")
    }
    func getSex() {
         print("boy")
    }
    func getAge(age: Int) {
        print("age = \(age)")
    }
}

var person = Person()
person.height   // 178
person.height = 180
person.height  // 180

person.weight // 120
// 可以更改(但在以前版本的Swift中是不能直接这样更改的
// 需要借助一个内部的stored property
// 然后把这个属性设计成一个computed property实现 get 和 set 方法)
person.weight = 130 // 130
// 当我们把person从Person转换成PersonProtocol时 他就是只读的了
// ❌Cannot assign to property: 'weight' is a get-only property
(person as PersonProtocol).weight = 120
如何定义可选的protocol属性或者方法?
@objc protocol PersonProtocol {
    optional var height: Int { get set }
    optional var weight: Int { get }
    optional func getName()
    optional func getSex()
    optional func getAge(age: Int)
}

class Person: PersonProtocol {
    // 如果想提供可选的约定方法或者属性那么只能定义@objc的protocol
    // 并且这种约定只能class能遵守
}
protocol可以继承,当然struct、class、enum都可以同时遵守多个约定
// 例如:
protocol PersonProtocol {
    var height: Int { get set }
    var weight: Int { get }
    func getName()
    func getSex()
    func getAge(age: Int)
}
protocol Engineer: PersonProtocol {
    var good: Bool { get }
}
protocol Animal {
}
struct Person: Engineer, Animal {
    // 省略了该实现约定的方法和属性
}

这些protocol需要我们掌握


CustomStringConvertible
struct Point {
    var x: Int
    var y: Int
}
let p = Point(x: 10, y: 10)
print(p) // Point(x: 10, y: 10)
// 如果我们想让打印界面变成 x = 10, y = 10 
// 那么我们就要遵守 CustomStringConvertible 这个 protocol
// 来看下实现
extension Point: CustomStringConvertible {
    // 这个 protocol 只有一个约定定义一个名为description的属性
    var description: String {
        return "x = \(self.x), y = \(self.y)"
    }
}
print(p) // x = 10, y = 10\n
BooleanType
// 如何更优雅的判断该点是不是原点(0, 0)
// 来一起看下
extension Point: BooleanType {
    // 遵守BooleanType这个protocol
    // 这个protocol也只需要一个实现一个名为boolValue的属性
    var boolValue: Bool {
        return self.x == 0 && self.y == 0
    }
}
let p = Point(x: 10, y: 10)
// 这样我们就能这样进行判断了
if p {
    print("是原点(0, 0)")
} else {
    print("不是原点")
}
// 这个protocol可以用到项目的好多地方,开发自己的脑洞去吧!
Equatable
extension Point: Equatable {}

func == (lP: Point, rP: Point) -> Bool {
    let equalX = lP.x == rP.x
    let equalY = lP.y == rP.y
    return equalX && equalY
}
let lP = Point(x: 10, y: 10)
let rP = Point(x: 10, y: 10)
// 不要以为是我们重载了 == 函数 就能自动推导出 !=
// 其实这都是 Equatable 这个 protocol 的功劳
// 当然我们如果要遵守 Equatable 这个 protocol 就必须实现 == 函 数 或者  != 函数
// 如果我们只是重载了 == 函数 是不能用 !=
if lP != rP {
    print("unequal")
} else {
    print("equal")
}
Comparable
// 想遵守 Comparable 这个 protocol 必须遵守 Equatable
// 同时用必须实现 < 函数 或者 > 函数
extension Point: Comparable {}
// 同样 > 函数会自动推导出来
func < (lP: Point, rP: Point) -> Bool {
    // 随意定义的规则 不必在意
    let x = lP.x - rP.x
    let y = lP.y - rP.y
    return x < y
}
// 实现了 Comparable 我们就可以把 Point 放到 Array 中,同时支持使用各种排序方法

protocol extension


protocol extension 最关键的一点就是能在 protocol extension 方法中获取 protocol 的属性,因为Swift编译器知道任何一个遵守 protocol 的自定义类型,一定会定义这个 protocol 约定的各种属性,既然这样我们就可以在 protocol extension 中添加默认的实现了。这也是为什么会有 protocol oriented programming 这个概念,但这时候肯定会有人说我通过面对对象的编程方式也可以实现,但为什么要用遵守 protocol 的方法呢,这个要等到了解 extension 中的 type constraints 后解释...
先看一个通过 protocol extension 添加默认实现的代码例子
// 定义一个人属性的 protocol
protocol PersonProperty {
    var height: Int { get } // cm
    var weight: Double { get } // kg
    // 判断体重是否合格的函数
    func isStandard() -> Bool
}
extension PersonProperty {
    // 给 protocol 添加默认的实现
    func isStandard() -> Bool {
        return self.weight == Double((height - 100)) * 0.9
    }
    // 给 protocol 添加默认属性
    var isPerfectHeight: Bool {
        return self.height == 178
    }
}
struct Person: PersonProperty {
    var height: Int
    var weight: Double
    // 如果自定义类型里面创建了遵守的 protocol 中的方法
    // 那么他将覆盖 protocol 中的方法
//    func isStandard() -> Bool {
//        return true
//    }
}
// 创建遵守 PersonProperty 的自定义类型
let p = Person(height: 178, weight: 61.5)
// 那么 p 这个自定义类型 天生就有判断这个人身高体重是否合格的方法
p.isStandard() // false
// 同样天生具有判断是否是 Perfect Height 的属性
p.isPerfectHeight // true

protocol extension 中的 type constraints


这相当于给 protocol extension 中的默认实现添加限定条件,写法如下
// 运动因素的 protocol
protocol SportsFactors {
    // 运动量
    var sportQuantity: Double { get }
}

// 下面这种写法就用到了 extension 中的 type constraints
// 意思是 只有同时遵守了 SportsFactors 和 PersonProperty 时
// 才使 PersonProperty 获得扩展 并提供带有 sportQuantity 属性的 isStandard 方法
extension PersonProperty where Self: SportsFactors {
    func isStandard() -> Bool {
        // 随意写的算法 不要在意
        return self.weight == Double((height - 100)) * 0.9 - self.sportQuantity
    }
}

protocol oriented programming 的优点


1、首先继承是 class 专有的,所以它不能用来扩展其他类型,但 protocol 是没有这种局限性的
2、试想一下,上面的代码你用面对对象的编程方式的话可能你就需要多一个运动量的属性,同时也要修改 isStandard 函数,一切看起来特别自然,随着后续需求的更改可能会有更多因素影响是否是合格的体重,那么这时候你就会在不知不觉中将你代码的耦合度成倍提高,其实对于这个类来说,他完全不需要知道是否是合格体重的计算细节,所以我们完全可以把这些类型无关的细节从类型定义上移出去,用一个 protocol 封装好这些细节,然后让其成为这个类型的一种修饰,这就是POP的核心思想。
3、当有多种因素制约是否是合格体重时,我们可以用多个 protocol 来对该类型进行修饰,每一种修饰的相关细节,我们都在对应的 protocol extension 中单独的封装起来,这样就大大降低了代码的耦合度,同时代码的可维护性也得到了相应的提高。swift标准库中大部分都是用这种思想构建的。
最终我们的写法可能是这样的
struct Person: PersonProperty, SportsFactors ... {
    var height: Int
    var weight: Double
    var sportQuantity: Double
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,022评论 4 62
  • 山王果刺梨是什么? 山王果刺梨源自于贵州黄龙山脉! 在那里空气清新!有着很神秘的大山! 刺梨的VC、VP、SOD含...
    山王果刺梨蜜阅读 4,209评论 0 0
  • 又见花落 周末,清晨,带孩子出门溜达,路旁,又见被昨夜风吹落的花瓣蜷了一地,当自行车...
    张语纯阅读 395评论 0 0
  • 在现今的社会中,有越来越壮大的一个群体就是江湖上流传的”剩女”,这一群体的主要特征就是年纪一大把,却未结婚. 而这...
    耀坤Rosy阅读 283评论 2 1
  • 奇葩说最新一期有史以来最沉重的一期 就是亲人得了绝症,打算放弃生命,我们该不该劝 当然,这场辩论没有胜负输赢 各持...
    大橙橙橙子阅读 500评论 0 0