协议(下)

在拓展中添加协议成员

通过扩展使得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. Office激活密钥

    Retail密钥: PHX9Q-N9GKW-CG4VF-MHCWR-367TX PB44J-GNX2R-BJJYX-HJW6R-Q9JP9 6PVPD-CNWDQ-G734C-DG7BM-VQTXK ...

  2. TCP 连接的建立和终止

    三路握手 建立一个TCP连接时会发生下述情形. (1)服务器必须准备好接受外来的连接.这通常通过调用socket.bind和listen这3个函数来完成的,我们称之为被动打开. (2)客户通过调用co ...

  3. OC学习笔记[注意事项]

    alloc  new  retain之后都必须要调用release方法 计数器要变只有这几种方法 retain release alloc new copy方法才会使计数器改变,谁想用人家对象,就对他 ...

  4. CI框架篇之视图篇--载入(1)

    创建视图 创建视图文件很简单,只需要建立后缀名为‘.php’的文件, 然后保存文件到 application/views/ 文件夹即可 当然,随着工程的大小,你有必要对很多的视图根据控制器进行归类, ...

  5. PHP 给前面或者后面添加0补位

    相信大家一定遇到这样的问题,因为PHP是弱类型的,所以进行排序的时候,有时候很胃疼 所以这里就需要将位数进行统一后进行处理 一般都是将末尾添加0进行补位 方法1 :  str_pad — 使用另一个字 ...

  6. HTML语言语法大全

    (文章转载至博客园 dodo-yufan) <! - - ... - -> 註解 <!> 跑馬燈 <marquee>...</marquee>普通捲動  ...

  7. [上传下载] C#FileDown文件下载类 (转载)

    点击下载 FileDown.zip 主要功能如下 .参数为虚拟路径 .获取物理地址 .普通下载 .分块下载 .输出硬盘文件,提供下载 支持大文件.续传.速度限制.资源占用小 看下面代码吧 /// &l ...

  8. C# DbHelperSQLP,操作不同的数据库帮助类 (转载)

    本类主要是用来访问不同数据库而编写的主要功能如下 .数据访问基础类(基于不同数据库),主要是用来访问不同数据库的. .得到最大值:是否存在:是否存在: . 执行SQL和Orace语句,返回影响的记录数 ...

  9. shiro认证

    一.通过ini文件初始化一个用户 1.通过ini配置文件创建securityManager2.调用subject.login方法主体提交认证,提交的token3.securityManager进行认证 ...

  10. jQuery.each的function中有哪些参数(可以大概理解function中的参数问题)

    1.没有参数 $("img").each(function(){ $(this).toggleClass("example"); }); 1 2 3 2.有一个 ...