21协议Word文档格式.docx
《21协议Word文档格式.docx》由会员分享,可在线阅读,更多相关《21协议Word文档格式.docx(25页珍藏版)》请在冰豆网上搜索。
o2.13.
继承
o2.14.
构造过程
o2.15.
析构过程
o2.16.
自动引用计数
o2.17.
可选链
o2.18.
类型转换
o2.19.
嵌套类型
o2.20.
扩展
o2.21.
协议
o2.22.
泛型
o2.23.
高级操作符
∙3.
语言参考
o3.1.
关于语言参考
o3.2.
词法结构
o3.3.
类型
o3.4.
表达式
o3.5.
语句
o3.6.
声明
o3.7.
特性
o3.8.
模式
o3.9.
泛型参数
o3.10.
语法总结
∙GeneratedusingGitBook
翻译:
geek5nan
校对:
dabing1022
本页包含内容:
∙协议的语法(ProtocolSyntax)
∙对属性的规定(PropertyRequirements)
∙对方法的规定(MethodRequirements)
∙对突变方法的的规定(MutatingMethodRequirements)
∙协议类型(ProtocolsasTypes)
∙委托(代理)模式(Delegation)
∙在扩展中添加协议成员(AddingProtocolConformancewithanExtension)
∙通过扩展补充协议声明(DeclaringProtocolAdoptionwithanExtension)
∙集合中的协议类型(CollectionsofProtocolTypes)
∙协议的继承(ProtocolInheritance)
∙协议合成(ProtocolComposition)
∙检验协议的一致性(CheckingforProtocolConformance)
∙对可选协议的规定(OptionalProtocolRequirements)
协议(Protocol)用于定义完成某项任务或功能所必须的方法和属性,协议实际上并不提供这些功能或任务的具体实现(Implementation)--而只用来描述这些实现应该是什么样的。
类,结构体,枚举通过提供协议所要求的方法,属性的具体实现来采用(adopt)协议。
任意能够满足协议要求的类型被称为协议的遵循者。
协议可以要求其遵循者提供特定的实例属性,实例方法,类方法,操作符或下标脚本等。
协议的语法
协议的定义方式与类,结构体,枚举的定义都非常相似,如下所示:
protocolSomeProtocol{
//协议内容
}
在类型名称后加上协议名称,中间以冒号:
分隔即可实现协议;
实现多个协议时,各协议之间用逗号,分隔,如下所示:
structSomeStructure:
FirstProtocol,AnotherProtocol{
//结构体内容
如果一个类在含有父类的同时也采用了协议,应当把父类放在所有的协议之前,如下所示:
classSomeClass:
SomeSuperClass,FirstProtocol,AnotherProtocol{
//类的内容
对属性的规定
协议可以规定其遵循者提供特定名称与类型的实例属性(instanceproperty)或类属性(typeproperty),而不管其是存储型属性(storedproperty)还是计算型属性(calculateproperty)。
此外也可以指定属性是只读的还是可读写的。
如果协议要求属性是可读写的,那么这个属性不能是常量存储型属性或只读计算型属性;
如果协议要求属性是只读的(gettable),那么计算型属性或存储型属性都能满足协议对属性的规定,在你的代码中,即使为只读属性实现了写方法(settable)也依然有效。
协议中的属性经常被加以var前缀声明其为变量属性,在声明后加上{setget}来表示属性是可读写的,只读的属性则写作{get},如下所示:
varmustBeSettable:
Int{getset}
vardoesNotNeedToBeSettable:
Int{get}
如下所示,通常在协议的定义中使用class前缀表示该属性为类成员;
在枚举和结构体实现协议时中,需要使用static关键字作为前缀。
protocolAnotherProtocol{
classvarsomeTypeProperty:
如下所示,这是一个含有一个实例属性要求的协议:
protocolFullyNamed{
varfullName:
String{get}
FullyNamed协议定义了任何拥有fullName的类型。
它并不指定具体类型,而只是要求类型必须提供一个fullName。
任何FullyNamed类型都得有一个只读的fullName属性,类型为String。
如下所示,这是一个实现了FullyNamed协议的简单结构体:
structPerson:
FullyNamed{
String
letjohn=Person(fullName:
"
JohnAppleseed"
)
//john.fullName为"
这个例子中定义了一个叫做Person的结构体,用来表示具有指定名字的人。
从第一行代码中可以看出,它采用了FullyNamed协议。
Person结构体的每一个实例都有一个叫做fullName,String类型的存储型属性,这正好匹配了FullyNamed协议的要求,也就意味着,Person结构体完整的遵循了协议。
(如果协议要求未被完全满足,在编译时会报错)
这有一个更为复杂的类,它采用并实现了FullyNamed协议,如下所示:
classStarship:
FullyNamed{
varprefix:
String?
varname:
init(name:
String,prefix:
=nil){
self.name=name
self.prefix=prefix
}
String{
return(prefix?
prefix!
+"
:
)+name
varncc1701=Starship(name:
Enterprise"
prefix:
USS"
//ncc1701.fullName=="
USSEnterprise"
Starship类把fullName属性实现为只读的计算型属性。
每一个Starship类的实例都有一个名为name的必备属性和一个名为prefix的可选属性。
当prefix存在时,将prefix插入到name之前来为Starship构建fullName,prefix不存在时,则将直接用name构建fullName
对方法的规定
协议可以要求其遵循者实现某些指定的实例方法或类方法。
这些方法作为协议的一部分,像普通的方法一样清晰的放在协议的定义中,而不需要大括号和方法体。
注意:
协议中的方法支持变长参数(variadicparameter),不支持参数默认值(defaultvalue)。
如下所示,协议中类方法的定义与类属性的定义相似,在协议定义的方法前置class关键字来表示。
当在枚举或结构体实现类方法时,需要使用static关键字来代替。
classfuncsomeTypeMethod()
如下所示,定义了含有一个实例方法的的协议。
protocolRandomNumberGenerator{
funcrandom()->
Double
RandomNumberGenerator协议要求其遵循者必须拥有一个名为random,返回值类型为Double的实例方法。
(尽管这里并未指明,但是我们假设返回值在[0,1]区间内)。
RandomNumberGenerator协议并不在意每一个随机数是怎样生成的,它只强调这里有一个随机数生成器。
如下所示,下边的是一个遵循了RandomNumberGenerator协议的类。
该类实现了一个叫做线性同余生成器(linearcongruentialgenerator)的伪随机数算法。
classLinearCongruentialGenerator:
RandomNumberGenerator{
varlastRandom=42.0
letm=139968.0
leta=3877.0
letc=29573.0
Double{
lastRandom=((lastRandom*a+c)%m)
returnlastRandom/m
letgenerator=LinearCongruentialGenerator()
println("
Here'
sarandomnumber:
\(generator.random())"
//输出:
0.37464991998171"
Andanotherone:
0.729023776863283"
对突变方法的规定
有时不得不在方法中更改实例的所属类型。
在基于值类型(valuetypes)(结构体,枚举)的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改实例及其属性的所属类型。
这一过程在ModifytingValueTypesfromWithinInstanceMethods章节中有详细描述。
如果协议中的实例方法打算改变其遵循者实例的类型,那么在协议定义时需要在方法前加mutating关键字,才能使结构体,枚举来采用并满足协议中对方法的规定。
注意:
用类实现协议中的mutating方法时,不用写mutating关键字;
用结构体,枚举实现协议中的mutating方法时,必须写mutating关键字。
如下所示,Togglable协议含有名为toggle的突变实例方法。
根据名称推测,toggle方法应该是用于切换或恢复其遵循者实例或其属性的类型。
protocolTogglable{
mutatingfunctoggle()
当使用枚举或结构体来实现Togglabl协议时,需要提供一个带有mutating前缀的toggle方法。
如下所示,OnOffSwitch枚举遵循了Togglable协议,On,Off两个成员用于表示当前状态。
枚举的toggle方法被标记为mutating,用以匹配Togglabel协议的规定。
enumOnOffSwitch:
Togglable{
caseOff,On
mutatingfunctoggle(){
switchself{
caseOff:
self=On
caseOn:
self=Off
varlightSwitch=OnOffSwitch.Off
lightSwitch.toggle()
//lightSwitch现在的值为.On
协议类型
尽管协议本身并不实现任何功能,但是协议可以被当做类型来使用。
使用场景:
∙协议类型作为函数、方法或构造器中的参数类型或返回值类型
∙协议类型作为常量、变量或属性的类型
∙协议类型作为数组、字典或其他容器中的元素类型
协议是一种类型,因此协议类型的名称应与其他类型(Int,Double,String)的写法相同,使用驼峰式写法
如下所示,这个示例中将协议当做类型来使用
classDice{
letsides:
Int
letgenerator:
RandomNumberGenerator
init(sides:
Int,generator:
RandomNumberGenerator){
self.sides=sides
self.generator=generator
funcroll()->
Int{
returnInt(generator.random()*Double(sides))+1
例子中又一个Dice类,用来代表桌游中的拥有N个面的骰子。
Dice的实例含有sides和generator两个属性,前者是整型,用来表示骰子有几个面,后者为骰子提供一个随机数生成器。
generator属性的类型为RandomNumberGenerator,因此任何遵循了RandomNumberGenerator协议的类型的实例都可以赋值给generator,除此之外,无其他要求。
Dice类中也有一个构造器(initializer),用来进行初始化操作。
构造器中含有一个名为generator,类型为RandomNumberGenerator的形参。
在调用构造方法时创建Dice的实例时,可以传入任何遵循RandomNumberGenerator协议的实例给generator。
Dice类也提供了一个名为roll的实例方法用来模拟骰子的面值。
它先使用generator的random方法来创建一个[0-1]区间内的随机数种子,然后加工这个随机数种子生成骰子的面值。
generator被认为是遵循了RandomNumberGenerator的类型,因而保证了random方法可以被调用。
如下所示,这里展示了如何使用LinearCongruentialGenerator的实例作为随机数生成器创建一个六面骰子:
vard6=Dice(sides:
6,generator:
LinearCongruentialGenerator())
for_in1...5{
println("
Randomdicerollis\(d6.roll())"
//输出结果
//Randomdicerollis3
//Randomdicerollis5
//Randomdicerollis4
委托(代理)模式
委托是一种设计模式(译者注:
想起了那年UITableViewDelegate中的奔跑,那是我逝去的Objective-C。
。
),它允许类或结构体将一些需要它们负责的功能交由(委托)给其他的类型的实例。
委托模式的实现很简单:
定义协议来封装那些需要被委托的函数和方法,使其遵循者拥有这些被委托的函数和方法。
委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的所属类型(译者注:
只要求外部数据源遵循某协议)。
下文是两个基于骰子游戏的协议:
protocolDiceGame{
vardice:
Dice{get}
funcplay()
protocolDiceGameDelegate{
funcgameDidStart(game:
DiceGame)
funcgame(game:
DiceGame,didStartNewTurnWithDiceRolldiceRoll:
Int)
funcgameDidEnd(game:
DiceGame协议可以在任意含有骰子的游戏中实现,DiceGameDelegate协议可以用来追踪DiceGame的游戏过程
如下所示,SnakesAndLadders是SnakesandLadders(译者注:
ControlFlow章节有该游戏的详细介绍)游戏的新版本。
新版本使用Dice作为骰子,并且实现了DiceGame和DiceGameDelegate协议,后者用来记录游戏的过程:
classSnakesAndLadders:
DiceGame{
letfinalSquare=25
letdice=Dice(sides:
6,generator:
varsquare=0
varboard:
Int[]
init(){
board=Int[](count:
finalSquare+1,repeatedValue:
0)
board[03]=+08;
board[06]=+11;
board[09]=+09;
board[10]=+02
board[14]=-10;
board[19]=-11;
board[22]=-02;
board[24]=-08
vardelegate:
DiceGameDelegate?
funcplay(){
square=0
delegate?
.gameDidStart(self)
gameLoop:
whilesquare!
=finalSquare{
letdiceRoll=dice.roll()
.game(self,didStartNewTurnWithDiceRoll:
diceRoll)
switchsquare+diceRoll{
casefinalSquare:
breakgameLoop
caseletnewSquarewherenewSquare>
finalSquare:
continuegameLoop
default:
square+=diceRoll
square+=board[square]
.gameDidEnd(self)
这个版本的游戏封装到了SnakesAndLadders类中,该类采用了DiceGame协议,并且提供了dice属性和play实例方法用来遵循协议。
(dice属性在构造之后就不在改变,且协议只要求dice为只读的,因此将dice声明为常量属性。
在SnakesAndLadders类的构造器(initializer)初始化游戏。
所有的游戏逻辑被转移到了play方法中,play方法使用协议规定的dice属性提供骰子摇出的值。
delegate并不是游戏的必备条件,因此delegate被定义为遵循DiceGameDelegate协议的可选属性,delegate使用nil作为初始值。
DicegameDelegate协议提供了三个方法用来追踪游戏过程。
被放置于游戏的逻辑中,即play()方法内。
分别在游戏开始时,新一轮开始时,游戏结束时被调用。
因为delegate是一个遵循DiceGameDelegate的可选属性,因此在play()方法中使用了可选链来调用委托方法。
若delegate属性为nil,则delegate所调用的方法失效。
若delegate不为nil,则方法能够被调用
如下所示,DiceGameTracker遵循了DiceGameDelegate协议
classDiceGameTracker:
DiceGameDelegate{
varnumberOfTurns=0
DiceGame){
numberOfTurns=0
ifgameisSnakesAndLadders{
StartedanewgameofSnakesandLadders"
Thegameisusinga\(game.dice.sides)-sideddice"
Int){
++numberOfTurns
Rolleda\(diceRoll)"
Thegamelastedfor\(numberOfTurns)turns"
DiceGameTracker实现了DiceGameDelegate协议规定的三个方法,用来记录游戏已经进行的轮数。
当游戏开始时,numberOfTurns属性被赋值为0;
在每新一轮中递加;
游戏结束后,输出打印游戏的总轮数。
gameDidStart方法从game参数获取游戏信息并输出。
game在方法中被当做DiceGame类型而不是SnakeAndLadders类型,所以方法中只能访问DiceGame协议中的成员。
当然了,这些方法也可以在类型转换之后调用。
在上例代码中,通过is操作符检查game是否为
SnakesAndLadders类型的实例,如果是,则打印出相应的内容。
无论当前进行的是何种游戏,game都遵循DiceGame协议以确保game含有dice属性,因此在gameDidStart方法中可以通过传入的game参数来访问dice属性,进而打印出dice的sides属性的值。
DiceGameTracker的运行情况,如下所示:
lettracker=DiceGameTracker()
letgame=SnakesAndLadders()
game.delegate=tracker
game.play()
//StartedanewgameofSnakesandLadders
//Thegameisusinga6-sideddice
//Rolleda3
//Rolleda5
//Rolleda4
//Thegamelastedfor4turns
在扩展中添加协议成员
即便无法修改源代码,依然可以通