协议(下)

在拓展中添加协议成员

通过扩展使得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. codeigniter nginx rewrite规则配置【转】

    转自:http://www.nginx.cn/1134.html nginx如何配置才能支持codeigniter ? 1. codeigniter的url美化去掉index.php   1 2 3 ...

  2. cf 85 E. Petya and Spiders

    http://codeforces.com/contest/112/problem/E 轮廓线dp.每一个格子中的蜘蛛选一个去向.终于,使每一个蜘蛛都有一个去向,同一时候保证有蜘蛛的格子最少.须要用4 ...

  3. 第33日 我疯了集成平台(六)-步履轻盈JQuery(四)

    6一个月28日本,天阴下雨. " 微雨过,小荷翻,榴花开欲燃.玉盆纤手弄清泉,琼珠碎却圆."          古老的JavaScript,且乱且复杂.封装成库,青春焕发,这样人们 ...

  4. swift 版本 UItableViewCell的动态高度补足

    用swift的朋友们很多都是从ios8开发了, 其中针对table cell高度自动计算的 UITableViewAutomaticDimension 异常好用,但好像只对uilabel对象有效    ...

  5. DTrace to Troubleshoot Java Native Memory Problems

    How to Use DTrace to Troubleshoot Java Native Memory Problems on Oracle Solaris 11 Hands-On Labs of ...

  6. iOS-SQLite数据库使用介绍

    iOS-SQLite数据库使用介绍 SQLite是MySQL的简化版,更多的运用与移动设备或小型设备上.SQLite的优点是具有可移植性,它不需要服务器就能运行,同时,它也存在一些缺陷,首先,没有提供 ...

  7. return与finally

    当return遇到了finally,先标记return的值,然后执行finally,当finally修改了return的值,那么执行finally后,传递最后一次return的值,若finally没有 ...

  8. 当winform窗体的Bordestyle设置为None时,鼠标可以拖动窗体的办法

    方法一: 1 2015-07-11 16:05:35 Point formPoint;//记录窗体的位置 private void Form1_MouseDown(object sender, Mou ...

  9. extSourceStat_7Day_Orders.php

    <?php /** Log文件格式2012/7/4 列号 字段含义 取值 ------------------------------------------------------------ ...

  10. php程序员的开始

    最近又懒惰了,博客没有更新,学习一直在停止,反思自己最近在学习什么了,但是脑子里面空白的一片,让我冒汗了.程序是一个不断的积累,最近在学习的路上,发现自己懂的越来越少,人就有点急躁了,什么都想学,导致 ...