协议(下)

在拓展中添加协议成员

通过扩展使得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. [翻译] GVUserDefaults

    GVUserDefaults Tired of writing all that code to get and set defaults in NSUserDefaults? Want to hav ...

  2. C#实现FTP文件夹下载功能【转载】

    网上有很多FTP单个文件下载的方法,前段时间需要用到一个FTP文件夹下载的功能,于是找了下网上的相关资料结合MSDN实现了一段FTP文件夹下载的代码. 实现的思路主要是通过遍历获得文件夹下的所有文件, ...

  3. nginx图片过滤处理模块http_image_filter_module安装配置笔记

    http_image_filter_module是nginx提供的集成图片处理模块,支持nginx-0.7.54以后的版本,在网站访问量不是很高磁盘有限不想生成多余的图片文件的前提下可,就可以用它实时 ...

  4. Mac OS X 中安装JDK7

    1. 下载 http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html 当前版本1.7.0_ ...

  5. ubuntu14.04使用root用户登录桌面 分类: 学习笔记 linux ubuntu 2015-07-05 10:30 199人阅读 评论(0) 收藏

    ubuntu安装好之后,默认是不能用root用户登录桌面的,只能使用普通用户或者访客登录.怎样开启root用户登录桌面呢? 先用普通用户登录,然后切换到root用户,然后执行如下命令: vi /usr ...

  6. XC软件管理器应用

    这是一个基于android 4.4开发的android应用-XC软件管理器.包含应用的信息查看,打开应用以及应用的卸载等功能.非常实用的一个应用,欢迎大家下载使用. 下载地址:http://downl ...

  7. AndroidStudio怎么将开源项目发布到jcenter

    前言 自己在网上搜了一大堆,大体就两种方法,而我选择的是其中代码少的的方法,不过他们或多或少留下了少许的坑,(按他们的方法我是上传成功,但不能发布到jCenter上去,也可能是我自己的问题o(≧v≦) ...

  8. shell中exit命令不退出脚本

    好久不用shell了,今天碰到一个坑,发现exit后,shell脚本还会运行. $JAVA_HOME/bin/jps | while read RES do PID=`echo $RES | awk ...

  9. Spring定时任务,Spring4整合quartz2.2,quartz-scheduler定时任务

    Spring4整合quartz2.2,quartz-scheduler定时任务,Spring定时任务 >>>>>>>>>>>>& ...

  10. Js的History对象

    History回顾 window.history表示window对象的历史记录 window.history的简单回顾 历史记录中前进/后退,移动到指定历史记录点 window.history.bac ...