协议(下)

在拓展中添加协议成员

通过扩展使得Dice类型遵循了一个新的协议,这和Dice类型在定义的时候声明为遵循TextRepresentable协议的效果相同。在扩展的时候,协议名称写在类型名之后,以冒号隔开,在大括号内写明新添加的协议内容。

protocol TextRepresentable {
func asText() -> String
} extension Dice: TextRepresentable { // 通过拓展让Dice类遵循TextRepresentable协议
func asText() -> String { // 实现协议方法
return "A \(sides)-sides dice"
}
} let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
dice.asText() // "A 6-sides dice"

通过拓展补充协议声明

当一个类型已经实现了协议中的所有要求,却没有声明为遵循该协议时,可以通过扩展(空的扩展体)来补充协议声明:

protocol TextRepresentable {
func asText() -> String
} struct Hamster {
var name: String
// 实现了协议方法,但是没有遵循协议
func asText() -> String {
return "A hamster named: \(name)"
}
} extension Hamster: TextRepresentable {} // 可以通过拓展是结构体遵循协议,因为实现了协议方法,所以只要写个空拓展就好。 // 通过拓展方式使Hamster遵循TextRepresentable,现在可以使用协议名作为类型
let simonTheHamster: Hamster = Hamster(name: "Simon")
let somethingTextRepresentable: TextRepresentable = simonTheHamster
somethingTextRepresentable.asText() // "A hamster named: Simon"

集合中的协议类型

协议类型可以在集合中使用,表示元素都遵循了一个协议

let things: [TextRepresentable] = [simonTheHamster, somethingTextRepresentable]
for thing in things {
thing.asText()
}

协议的继承

协议能够继承一个或多个其他协议,可以在继承的协议基础上增加新的内容要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔:

protocol TextRepresentable {
func asText() -> String
} protocol PrettyTextRepresentable: TextRepresentable {
func asPrettyText() -> String
}

例子

extension SnakesAndLadders: PrettyTextRepresentable {
func asText() -> String {
return "A game of Snakes and Ladders with \(finalSquare) squares"
} func asPrettyText() -> String {
var output = asText() + ":\n"
for index in 1...finalSquare {
switch board[index] {
case let ladder where ladder > 0:
output += "↑ "
case let snake where snake < 0:
output += "↓ "
default:
output += "○ "
}
}
return output
}
} print(game.asPrettyText())

类专属协议

你可以在协议的继承列表中,通过添加class关键字,限制协议只能适配到类(class)类型。(结构体或枚举不能遵循该协议)。该class关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。

protocol SomeInheritedProtocol {

}

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// 只允许类遵循的协议
}

协议合成

有时候需要同时遵循多个协议。你可以将多个协议采用protocol<SomeProtocol, AnotherProtocol>这样的格式进行组合,称为协议合成。你可以在<>中罗列任意多个你想要遵循的协议,以逗号分隔。

注意:协议合成并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。

protocol Named: class {
var name: String { get }
} protocol Aged: class {
var age: Int { get }
} class Person: Aged, Named {
let name: String
let age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
} let p1 = Person(name: "Alex", age: 23) func wishHappyBirthday(celebrator: protocol<Named, Aged>) { // 参数类型规定要同时遵守两个协议的实例
print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
} wishHappyBirthday(p1)
// "Happy birthday Alex - you're 23!"

检查协议的一致性

你可以使用isas操作符来检查是否遵循某一协议或强制转化为某一类型。检查和转化的语法和之前相同。

  • is操作符用来检查实例是否遵循了某个协议
  • as?返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil
  • as用以强制向下转型,如果强转失败,会引起运行时错误。
protocol HasArea {
var area: Double { get }
} class Circle: HasArea {
let pi = 3.1415926
var radius: Double
var area: Double {
return pi * radius * radius
}
init(radius: Double){
self.radius = radius
}
} class Country: HasArea {
var area: Double
init(area: Double){
self.area = area
}
} class Animal { // Animal没有继承HasArea
var legs: Int
init(legs: Int){
self.legs = legs
}
} let objects: [AnyObject] = [
Circle(radius: 2.0),
Country(area: 200_000),
Animal(legs: 4)
] for object in objects {
if let objectWithArea = object as? HasArea {
print("Area is \(objectWithArea.area)")
} else {
print("Something that doesn't have an area")
}
}

对可选协议的规定

协议可以含有可选成员,其遵循者可以选择是否实现这些成员。在协议中使用optional关键字作为前缀来定义可选成员。

可选协议在调用时使用可选链,因为协议的遵循者可能没有实现可选内容。

someOptionalMethod?(someArgument)这样,你可以在可选方法名称后加上?来检查该方法是否被实现。可选方法和可选属性都会返回一个可选值(optional value),当其不可访问时,?之后语句不会执行,并整体返回nil

注意:可选协议只能在含有@objc前缀的协议中生效。且@objc的协议只能被遵循这个前缀表示协议将暴露给Objective-C代码。即使你不打算和Objective-C有什么交互,如果你想要指明协议包含可选属性,那么还是要加上@obj前缀

@objc protocol CounterDataSource {
optional func incrementForCount(count: Int) -> Int
optional var fixedIncrement: Int { get }
} class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.incrementForCount?(count) {
count += amount
} else if let amount = dataSource?.fixedIncrement {
count += amount
}
}
} class ThreeSource: CounterDataSource {
@objc var fixedIncrement: Int = 3
} var counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
counter.increment()
print(counter.count)
  // 3 6 9 12
}
class TowardsZeroSource: CounterDataSource {
@objc func incrementForCount(count: Int) -> Int {
switch count {
case 0:
return 0
case let mCount where mCount > 0:
return -1
default:
return 1
}
}
} let counter = Counter()
counter.count = -4
counter.dataSource = TowardsZeroSource()
for _ in 1...5 {
counter.increment()
print(counter.count)
// -3 -2 -1 0 0
}

协议扩展

使用扩展协议的方式可以为遵循者提供方法或属性的实现。通过这种方式,可以让你无需在每个遵循者中都实现一次,无需使用全局函数,你可以通过扩展协议的方式进行定义。

protocol RandomNumberGenerator {
func random() -> Double
} 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) % m)
return lastRandom / m
}
} extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
} let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Here's a random number: 0.37464991998171
print("And here's a random Boolean: \(generator.randomBool())")
// And here's a random Boolean: true

提供默认实现

可以通过协议扩展的方式来为协议规定的属性和方法提供默认的实现。如果协议的遵循者对规定的属性和方法提供了自己的实现,那么遵循者提供的实现将被使用。

注意:通过扩展协议提供的协议实现和可选协议规定有区别。虽然协议遵循者无需自己实现,通过扩展提供的默认实现,可以不是用可选链调用。

protocol TextRepresentable {
func asText() -> String
} protocol PrettyTextRepresentable: TextRepresentable {
func asPrettyText() -> String var someProperty: String { get }
} extension PrettyTextRepresentable {
func asPrettyText() -> String {
return asText() + " some text"
} var someProperty: String {
return ""
}
} class SomeClass: PrettyTextRepresentable {
var name = "Tom" func asText() -> String {
return name
}
} let person = SomeClass()
print(person.asPrettyText())
print(person.someProperty)

为协议扩展添加限制条件

在扩展协议的时候,可以指定一些限制,只有满足这些限制的协议遵循者,才能获得协议扩展提供的属性和方法。这些限制写在协议名之后,使用where关键字来描述限制情况。

protocol TextRepresentable {
func asText() -> String
} struct Hamster: TextRepresentable {
var name: String
func asText() -> String {
return "A hamster named \(name)"
}
} // 拓展 CollectionType 协议,当元素遵循 TextRepresentable 协议时,可用拓展中的功能
extension CollectionType where Generator.Element : TextRepresentable {
func asList() -> String {
return "some string"
}
} let murrayTheHamster = Hamster(name: "Murray")
let morganTheHamster = Hamster(name: "Morgan")
let mauriceTheHamster = Hamster(name: "Maurice")
let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]
// 因为Array遵循CollectionType协议,数组的元素又遵循TextRepresentable协议,所以数组可以使用asList()方法得到数组内容的文本表示
print(hamsters.asList())

学习Swift -- 协议(下)的更多相关文章

  1. 学习Swift -- 协议(上)

    协议(上) 协议是Swift非常重要的部分,协议规定了用来实现某一特定工作或者功能所必需的方法和属性.类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能.任意能够满足协议要求 ...

  2. 学习Swift -- 构造器(下)

    构造器(下) 可失败的构造器 如果一个类,结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器,是非常有必要的.这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某 ...

  3. ios -- 教你如何轻松学习Swift语法(三) 完结篇

    前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦.      一.自动引用计数   1.自动引用计数工作机制      1.1 swift和o ...

  4. ios -- 教你如何轻松学习Swift语法(二)

    前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦.      一.可选类型(重点内容)   1.什么是可选类型?        1.1在OC开 ...

  5. ios -- 教你如何轻松学习Swift语法(一)

    目前随着公司开发模式的变更,swift也显得越发重要,相对来说,swift语言更加简洁,严谨.但对于我来说,感觉swift细节的处理很繁琐,可能是还没适应的缘故吧.基本每写一句代码,都要对变量的数据类 ...

  6. JavaWeb学习----http协议

    一.什么是HTTP协议: 1.概念: 客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式(规定客户端和服务器如何 ...

  7. 学习swift语言的快速入门教程推荐

    随着苹果产品越来越火爆,苹果新推出的swift必定将在很大程度上代替oc语言.学好swift语言,对于IOS工程师来讲,已经是一门必备技能. 有一些比较好的英文版教程,值得学习. 1. Swift T ...

  8. 协议基础:SMTP:使用Telnet学习SMTP协议

    协议基础:SMTP:使用Telnet学习SMTP协议 2018-07-30 20:05:50 liumiaocn 阅读数 7479更多 分类专栏: 工具 Unix/Linux   版权声明:本文为博主 ...

  9. Swift-如何快速学习Swift

    关于本文: 1.说明本文写作的目的 2.整理了Swift的基本语法树 3.看图作文 一.写作目的 昨天看了一个知识专栏,作者讲述的是“如何研究性的学习”.整个课程1个小时9分钟,花了我19块人民币.其 ...

随机推荐

  1. jquery-data的三种用法

    1.jquery-data的用处 jQuery-data主要是用来存储数据,帮助普通对象或者jQuery对象来存储数据,其实如果单纯的储存dom的单一的属性,用attr自定义属性足够了:如果存储多个键 ...

  2. 基于SAML的单点登录介绍

    http://blog.csdn.net/csethcrm/article/details/20694993 一.背景知识: SAML即安全断言标记语言,英文全称是Security Assertion ...

  3. JavaScript网站设计实践(七)编写最后一个页面 改进表单

    一.最后一个页面 contact.html.改进表单 在该页面实现的功能: 几乎所有的网站都会有表单填写,对于用户输入和填写的数据,首先我们一般现在前台验证,然后再去后台验证. 在前台最简单的验证:检 ...

  4. Android_设备隐私获取,忽略6.0权限管理

    1.前言 (1).由于MIUI等部分国产定制系统也有权限管理,没有相关api,故无法判断用户是否允许获取联系人等隐私.在Android 6.0之后,新增权限管理可以通过官方api判断用户的运行状态: ...

  5. careercup-链表 2.2

    2.2 实现一个算法,找到单链表中倒数第k个节点. 这道题的考点在于我们怎么在一个单链表中找到倒数第n个元素? 由于是单链表,所以我们没办法从最后一个元素数起,然后数n个得到答案. 但这种最直观的思路 ...

  6. labview中层叠式顺序结构与平铺式顺序结构有什么不同?

    也就看着不同,平铺式看着直观,但比较占地方,程序复杂了就显得过大.二者可互相转换,从这点也可以看出它们没有本质不同!

  7. 固定ip

    192.168.1.111 255.255.255.0 192.168.1.1 8.8.8.8 202.96.134.33

  8. 使用Navicat for Oracle 出现的错误

    错误提示: 意思是不能创建oci环境我们需要对 Navicat for Oracle 做一下配置依此选择 Tools -> Miscellaneous -> OCI OCI library ...

  9. JS获取页面上所有input

    for (var i = 0; i < document.getElementsByTagName("input").length; i++) { if (document. ...

  10. FXBlurView用法

    FXBlurView是UIView的子类,它实现毛玻璃效果的原理其实就是覆盖上一层FXBlurView的实例对象. - (void)viewDidLoad { [super viewDidLoad]; ...