make gitbook
This commit is contained in:
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.21" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.21" data-basepath=".." data-revision="1402759431779">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,11 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_74">
|
||||
<section class="normal" id="section-gitbook_550">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:geek5nan</p>
|
||||
<p>校对:dabing1022</p>
|
||||
<p>翻译:geek5nan
|
||||
校对:dabing1022</p>
|
||||
</blockquote>
|
||||
<h1 id="-">协议</h1>
|
||||
<hr>
|
||||
@ -604,7 +604,7 @@
|
||||
<li><a href="#protocols_as_types">协议类型(Protocols as Types)</a></li>
|
||||
<li><a href="#delegation">委托(代理)模式(Delegation)</a></li>
|
||||
<li><a href="#adding_protocol_conformance_with_an_extension">在扩展中添加协议成员(Adding Protocol Conformance with an Extension)</a></li>
|
||||
<li><a href="#declaring_protocol_adoption_with_an_extension">通过延展补充协议声明(Declaring Protocol Adoption with an Extension)</a></li>
|
||||
<li><a href="#declaring_protocol_adoption_with_an_extension">通过扩展补充协议声明(Declaring Protocol Adoption with an Extension)</a></li>
|
||||
<li><a href="#collections_of_protocol_types">集合中的协议类型(Collections of Protocol Types)</a></li>
|
||||
<li><a href="#protocol_inheritance">协议的继承(Protocol Inheritance)</a></li>
|
||||
<li><a href="#protocol_composition">协议合成(Protocol Composition)</a></li>
|
||||
@ -616,42 +616,49 @@
|
||||
<p><a name="protocol_syntax"></a></p>
|
||||
<h2 id="-">协议的语法</h2>
|
||||
<p><code>协议</code>的定义与类,结构体,枚举的定义非常相似,如下所示:</p>
|
||||
<pre><code>protocol SomeProtocol {
|
||||
<pre><code class="lang-swift">protocol SomeProtocol {
|
||||
// 协议内容
|
||||
}
|
||||
</code></pre><p>在类,结构体,枚举的名称后加上<code>协议名称</code>,中间以冒号<code>:</code>分隔即可实现协议;实现多个协议时,各协议之间用逗号<code>,</code>分隔,如下所示:</p>
|
||||
<pre><code>struct SomeStructure: FirstProtocol, AnotherProtocol {
|
||||
</code></pre>
|
||||
<p>在类,结构体,枚举的名称后加上<code>协议名称</code>,中间以冒号<code>:</code>分隔即可实现协议;实现多个协议时,各协议之间用逗号<code>,</code>分隔,如下所示:</p>
|
||||
<pre><code class="lang-swift">struct SomeStructure: FirstProtocol, AnotherProtocol {
|
||||
// 结构体内容
|
||||
}
|
||||
</code></pre><p>当某个类含有父类的同时并实现了协议,应当把父类放在所有的协议之前,如下所示:</p>
|
||||
<pre><code>class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
|
||||
</code></pre>
|
||||
<p>当某个类含有父类的同时并实现了协议,应当把父类放在所有的协议之前,如下所示:</p>
|
||||
<pre><code class="lang-swift">class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
|
||||
// 类的内容
|
||||
}
|
||||
</code></pre><p><a name="property_requirements"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="property_requirements"></a></p>
|
||||
<h2 id="-">属性要求</h2>
|
||||
<p><code>协议</code>能够要求其<code>遵循者</code>必须含有一些<strong>特定名称和类型</strong>的<code>实例属性(instance property)</code>或<code>类属性 (type property)</code>,也能够要求属性的<code>(设置权限)settable</code> 和<code>(访问权限)gettable</code>,但它不要求<code>属性</code>是<code>存储型属性(stored property)</code>还是<code>计算型属性(calculate property)</code>。</p>
|
||||
<p><code>协议</code>能够要求其<code>遵循者</code>必须含有一些<strong>特定名称和类型</strong>的<code>实例属性(instance property)</code>或<code>类属性 (type property)</code>,也能够要求属性具有<code>(设置权限)settable</code> 和<code>(访问权限)gettable</code>,但它不要求<code>属性</code>是<code>存储型属性(stored property)</code>还是<code>计算型属性(calculate property)</code>。</p>
|
||||
<p>如果协议要求属性具有设置权限和访问权限,那常量存储型属性或者只读计算型属性都无法满足此要求。如果协议只要求属性具有访问权限,那任何类型的属性都可以满足此要求,无论这些属性是否具有设置权限。</p>
|
||||
<p>通常前置<code>var</code>关键字将属性声明为变量。在属性声明后写上<code>{ get set }</code>表示属性为可读写的。<code>{ get }</code>用来表示属性为可读的。即使你为可读的属性实现了<code>setter</code>方法,它也不会出错。</p>
|
||||
<pre><code>protocol SomeProtocol {
|
||||
<pre><code class="lang-swift">protocol SomeProtocol {
|
||||
var musBeSettable : Int { get set }
|
||||
var doesNotNeedToBeSettable: Int { get }
|
||||
}
|
||||
</code></pre><p>用类来实现协议时,使用<code>class</code>关键字来表示该属性为类成员;用结构体或枚举实现协议时,则使用<code>static</code>关键字来表示:</p>
|
||||
<pre><code>protocol AnotherProtocol {
|
||||
</code></pre>
|
||||
<p>用类来实现协议时,使用<code>class</code>关键字来表示该属性为类成员;用结构体或枚举实现协议时,则使用<code>static</code>关键字来表示:</p>
|
||||
<pre><code class="lang-swift">protocol AnotherProtocol {
|
||||
class var someTypeProperty: Int { get set }
|
||||
}
|
||||
|
||||
protocol FullyNamed {
|
||||
var fullName: String { get }
|
||||
}
|
||||
</code></pre><p><code>FullyNamed</code>协议含有<code>fullName</code>属性。因此其<code>遵循者</code>必须含有一个名为<code>fullName</code>,类型为<code>String</code>的可读属性。</p>
|
||||
<pre><code>struct Person: FullyNamed{
|
||||
</code></pre>
|
||||
<p><code>FullyNamed</code>协议含有<code>fullName</code>属性。因此其<code>遵循者</code>必须含有一个名为<code>fullName</code>,类型为<code>String</code>的可读属性。</p>
|
||||
<pre><code class="lang-swift">struct Person: FullyNamed{
|
||||
var fullName: String
|
||||
}
|
||||
let john = Person(fullName: "John Appleseed")
|
||||
//john.fullName 为 "John Appleseed"
|
||||
</code></pre><p><code>Person</code>结构体含有一个名为<code>fullName</code>的<code>存储型属性</code>,完整的<code>遵循</code>了协议。(<em>若协议未被完整遵循,编译时则会报错</em>)。</p>
|
||||
</code></pre>
|
||||
<p><code>Person</code>结构体含有一个名为<code>fullName</code>的<code>存储型属性</code>,完整的<code>遵循</code>了协议。(<em>若协议未被完整遵循,编译时则会报错</em>)。</p>
|
||||
<p>如下所示,<code>Startship</code>类<code>遵循</code>了<code>FullyNamed</code>协议:</p>
|
||||
<pre><code>class Starship: FullyNamed {
|
||||
<pre><code class="lang-swift">class Starship: FullyNamed {
|
||||
var prefix: String?
|
||||
var name: String
|
||||
init(name: String, prefix: String? = nil ) {
|
||||
@ -664,25 +671,27 @@ let john = Person(fullName: "John Appleseed")
|
||||
}
|
||||
var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
|
||||
// ncc1701.fullName == "USS Enterprise"
|
||||
</code></pre><p><code>Starship</code>类将<code>fullName</code>实现为可读的<code>计算型属性</code>。它的每一个实例都有一个名为<code>name</code>的必备属性和一个名为<code>prefix</code>的可选属性。 当<code>prefix</code>存在时,将<code>prefix</code>插入到<code>name</code>之前来为<code>Starship</code>构建<code>fullName</code>。</p>
|
||||
</code></pre>
|
||||
<p><code>Starship</code>类将<code>fullName</code>实现为可读的<code>计算型属性</code>。它的每一个实例都有一个名为<code>name</code>的必备属性和一个名为<code>prefix</code>的可选属性。 当<code>prefix</code>存在时,将<code>prefix</code>插入到<code>name</code>之前来为<code>Starship</code>构建<code>fullName</code>。</p>
|
||||
<p><a name="method_requirements"></a></p>
|
||||
<h2 id="-">方法要求</h2>
|
||||
<p><code>协议</code>能够要求其<code>遵循者</code>必备某些特定的<code>实例方法</code>和<code>类方法</code>。协议方法的声明与普通方法声明相似,但它不需要<code>方法</code>内容。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>协议方法支持<code>变长参数(variadic parameter)</code>,不支持<code>默认参数(default parameter)</code>。</p>
|
||||
<p>注意:
|
||||
协议方法支持<code>变长参数(variadic parameter)</code>,不支持<code>默认参数(default parameter)</code>。</p>
|
||||
</blockquote>
|
||||
<p>前置<code>class</code>关键字表示协议中的成员为<code>类成员</code>;当协议用于被<code>枚举</code>或<code>结构体</code>遵循时,则使用<code>static</code>关键字。如下所示:</p>
|
||||
<pre><code>protocol SomeProtocol {
|
||||
<pre><code class="lang-swift">protocol SomeProtocol {
|
||||
class func someTypeMethod()
|
||||
}
|
||||
|
||||
protocol RandomNumberGenerator {
|
||||
func random() -> Double
|
||||
}
|
||||
</code></pre><p><code>RandomNumberGenerator</code>协议要求其<code>遵循者</code>必须拥有一个名为<code>random</code>, 返回值类型为<code>Double</code>的实例方法。(我们假设随机数在[0,1]区间内)。</p>
|
||||
</code></pre>
|
||||
<p><code>RandomNumberGenerator</code>协议要求其<code>遵循者</code>必须拥有一个名为<code>random</code>, 返回值类型为<code>Double</code>的实例方法。(我们假设随机数在[0,1]区间内)。</p>
|
||||
<p><code>LinearCongruentialGenerator</code>类<code>遵循</code>了<code>RandomNumberGenerator</code>协议,并提供了一个叫做<em>线性同余生成器(linear congruential generator)</em>的伪随机数算法。</p>
|
||||
<pre><code>class LinearCongruentialGenerator: RandomNumberGenerator {
|
||||
<pre><code class="lang-swift">class LinearCongruentialGenerator: RandomNumberGenerator {
|
||||
var lastRandom = 42.0
|
||||
let m = 139968.0
|
||||
let a = 3877.0
|
||||
@ -697,21 +706,23 @@ println("Here's a random number: \(generator.random())")
|
||||
// 输出 : "Here's a random number: 0.37464991998171"
|
||||
println("And another one: \(generator.random())")
|
||||
// 输出 : "And another one: 0.729023776863283"
|
||||
</code></pre><p><a name="mutating_method_requirements"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="mutating_method_requirements"></a></p>
|
||||
<h2 id="-">突变方法要求</h2>
|
||||
<p>能在<code>方法</code>或<code>函数</code>内部改变实例类型的方法称为<code>突变方法</code>。在<code>值类型(Value Type)</code>(<em>译者注:特指结构体和枚举</em>)中的的<code>函数</code>前缀加上<code>mutating</code>关键字来表示该函数允许改变该实例和其属性的类型。 这一变换过程在<a href="11_Methods.html#instance_methods">实例方法(Instance Methods)</a>章节中有详细描述。</p>
|
||||
<p>(<em>译者注:类中的成员为<code>引用类型(Reference Type)</code>,可以方便的修改实例及其属性的值而无需改变类型;而<code>结构体</code>和<code>枚举</code>中的成员均为<code>值类型(Value Type)</code>,修改变量的值就相当于修改变量的类型,而<code>Swift</code>默认不允许修改类型,因此需要前置<code>mutating</code>关键字用来表示该<code>函数</code>中能够修改类型</em>)</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>用<code>class</code>实现协议中的<code>mutating</code>方法时,不用写<code>mutating</code>关键字;用<code>结构体</code>,<code>枚举</code>实现协议中的<code>mutating</code>方法时,必须写<code>mutating</code>关键字。</p>
|
||||
<p>注意:
|
||||
用<code>class</code>实现协议中的<code>mutating</code>方法时,不用写<code>mutating</code>关键字;用<code>结构体</code>,<code>枚举</code>实现协议中的<code>mutating</code>方法时,必须写<code>mutating</code>关键字。</p>
|
||||
</blockquote>
|
||||
<p>如下所示,<code>Togglable</code>协议含有<code>toggle</code>函数。根据函数名称推测,<code>toggle</code>可能用于<strong>切换或恢复</strong>某个属性的状态。<code>mutating</code>关键字表示它为<code>突变方法</code>:</p>
|
||||
<pre><code>protocol Togglable {
|
||||
<pre><code class="lang-swift">protocol Togglable {
|
||||
mutating func toggle()
|
||||
}
|
||||
</code></pre><p>当使用<code>枚举</code>或<code>结构体</code>来实现<code>Togglabl</code>协议时,必须在<code>toggle</code>方法前加上<code>mutating</code>关键字。</p>
|
||||
</code></pre>
|
||||
<p>当使用<code>枚举</code>或<code>结构体</code>来实现<code>Togglabl</code>协议时,必须在<code>toggle</code>方法前加上<code>mutating</code>关键字。</p>
|
||||
<p>如下所示,<code>OnOffSwitch</code>枚举<code>遵循</code>了<code>Togglable</code>协议,<code>On</code>,<code>Off</code>两个成员用于表示当前状态</p>
|
||||
<pre><code>enum OnOffSwitch: Togglable {
|
||||
<pre><code class="lang-swift">enum OnOffSwitch: Togglable {
|
||||
case Off, On
|
||||
mutating func toggle() {
|
||||
switch self {
|
||||
@ -725,7 +736,8 @@ println("And another one: \(generator.random())")
|
||||
var lightSwitch = OnOffSwitch.Off
|
||||
lightSwitch.toggle()
|
||||
//lightSwitch 现在的值为 .On
|
||||
</code></pre><p><a name="protocols_as_types"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="protocols_as_types"></a></p>
|
||||
<h2 id="-">协议类型</h2>
|
||||
<p><code>协议</code>本身不实现任何功能,但你可以将它当做<code>类型</code>来使用。</p>
|
||||
<p>使用场景:</p>
|
||||
@ -735,10 +747,10 @@ lightSwitch.toggle()
|
||||
<li>作为数组,字典或其他容器中的元素类型</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>协议类型应与其他类型(Int,Double,String)的写法相同,使用驼峰式</p>
|
||||
<p>注意:
|
||||
协议类型应与其他类型(Int,Double,String)的写法相同,使用驼峰式</p>
|
||||
</blockquote>
|
||||
<pre><code>class Dice {
|
||||
<pre><code class="lang-swift">class Dice {
|
||||
let sides: Int
|
||||
let generator: RandomNumberGenerator
|
||||
init(sides: Int, generator: RandomNumberGenerator) {
|
||||
@ -749,12 +761,13 @@ lightSwitch.toggle()
|
||||
return Int(generator.random() * Double(sides)) +1
|
||||
}
|
||||
}
|
||||
</code></pre><p>这里定义了一个名为 <code>Dice</code>的类,用来代表桌游中的N个面的骰子。</p>
|
||||
<p> <code>Dice</code>含有<code>sides</code>和<code>generator</code>两个属性,前者用来表示骰子有几个面,后者为骰子提供一个随机数生成器。由于后者为<code>RandomNumberGenerator</code>的协议类型。所以它能够被赋值为任意<code>遵循</code>该协议的类型。</p>
|
||||
</code></pre>
|
||||
<p>这里定义了一个名为 <code>Dice</code>的类,用来代表桌游中的N个面的骰子。</p>
|
||||
<p><code>Dice</code>含有<code>sides</code>和<code>generator</code>两个属性,前者用来表示骰子有几个面,后者为骰子提供一个随机数生成器。由于后者为<code>RandomNumberGenerator</code>的协议类型。所以它能够被赋值为任意<code>遵循</code>该协议的类型。</p>
|
||||
<p>此外,使用<code>构造器(init)</code>来代替之前版本中的<code>setup</code>操作。构造器中含有一个名为<code>generator</code>,类型为<code>RandomNumberGenerator</code>的形参,使得它可以接收任意遵循<code>RandomNumberGenerator</code>协议的类型。</p>
|
||||
<p><code>roll</code>方法用来模拟骰子的面值。它先使用<code>generator</code>的<code>random</code>方法来创建一个[0-1]区间内的随机数种子,然后加工这个随机数种子生成骰子的面值。</p>
|
||||
<p>如下所示,<code>LinearCongruentialGenerator</code>的实例作为随机数生成器传入<code>Dice</code>的<code>构造器</code></p>
|
||||
<pre><code>var d6 = Dice(sides: 6,generator: LinearCongruentialGenerator())
|
||||
<pre><code class="lang-swift">var d6 = Dice(sides: 6,generator: LinearCongruentialGenerator())
|
||||
for _ in 1...5 {
|
||||
println("Random dice roll is \(d6.roll())")
|
||||
}
|
||||
@ -764,24 +777,27 @@ for _ in 1...5 {
|
||||
//Random dice roll is 4
|
||||
//Random dice roll is 5
|
||||
//Random dice roll is 4
|
||||
</code></pre><p><a name="delegation"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="delegation"></a></p>
|
||||
<h2 id="-">委托(代理)模式</h2>
|
||||
<p>委托是一种设计模式,它允许类或结构体将一些需要它们负责的功能<code>交由(委托)</code>给其他的类型。</p>
|
||||
<p>委托模式的实现很简单: 定义<code>协议</code>来<code>封装</code>那些需要被委托的<code>函数和方法</code>, 使其<code>遵循者</code>拥有这些被委托的<code>函数和方法</code>。</p>
|
||||
<p>委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的类型。</p>
|
||||
<p>下文是两个基于骰子游戏的协议:</p>
|
||||
<pre><code>protocol DiceGame {
|
||||
<pre><code class="lang-swift">protocol DiceGame {
|
||||
var dice: Dice { get }
|
||||
func play()
|
||||
}
|
||||
|
||||
protocol DiceGameDelegate {
|
||||
func gameDidStart(game: DiceGame)
|
||||
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll:Int)
|
||||
func gameDidEnd(game: DiceGame)
|
||||
}
|
||||
</code></pre><p><code>DiceGame</code>协议可以在任意含有骰子的游戏中实现,<code>DiceGameDelegate</code>协议可以用来追踪<code>DiceGame</code>的游戏过程。</p>
|
||||
</code></pre>
|
||||
<p><code>DiceGame</code>协议可以在任意含有骰子的游戏中实现,<code>DiceGameDelegate</code>协议可以用来追踪<code>DiceGame</code>的游戏过程。</p>
|
||||
<p>如下所示,<code>SnakesAndLadders</code>是<code>Snakes and Ladders</code>(译者注:<a href="05_Control_Flow.html">控制流</a>章节有该游戏的详细介绍)游戏的新版本。新版本使用<code>Dice</code>作为骰子,并且实现了<code>DiceGame</code>和<code>DiceGameDelegate</code>协议</p>
|
||||
<pre><code>class SnakesAndLadders: DiceGame {
|
||||
<pre><code class="lang-swift">class SnakesAndLadders: DiceGame {
|
||||
let finalSquare = 25
|
||||
let dic = Dice(sides: 6, generator: LinearCongruentialGenerator())
|
||||
var square = 0
|
||||
@ -811,15 +827,16 @@ protocol DiceGameDelegate {
|
||||
delegate?.gameDIdEnd(self)
|
||||
}
|
||||
}
|
||||
</code></pre><p>游戏的<code>初始化设置(setup)</code>被<code>SnakesAndLadders</code>类的<code>构造器(initializer)</code>实现。所有的游戏逻辑被转移到了<code>play</code>方法中。</p>
|
||||
</code></pre>
|
||||
<p>游戏的<code>初始化设置(setup)</code>被<code>SnakesAndLadders</code>类的<code>构造器(initializer)</code>实现。所有的游戏逻辑被转移到了<code>play</code>方法中。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>因为<code>delegate</code>并不是该游戏的必备条件,<code>delegate</code>被定义为遵循<code>DiceGameDelegate</code>协议的可选属性</p>
|
||||
<p>注意:
|
||||
因为<code>delegate</code>并不是该游戏的必备条件,<code>delegate</code>被定义为遵循<code>DiceGameDelegate</code>协议的可选属性</p>
|
||||
</blockquote>
|
||||
<p><code>DicegameDelegate</code>协议提供了三个方法用来追踪游戏过程。被放置于游戏的逻辑中,即<code>play()</code>方法内。分别在游戏开始时,新一轮开始时,游戏结束时被调用。</p>
|
||||
<p>因为<code>delegate</code>是一个遵循<code>DiceGameDelegate</code>的可选属性,因此在<code>play()</code>方法中使用了<code>可选链</code>来调用委托方法。 若<code>delegate</code>属性为<code>nil</code>, 则委托调用<em>优雅地</em>失效。若<code>delegate</code>不为<code>nil</code>,则委托方法被调用</p>
|
||||
<p>如下所示,<code>DiceGameTracker</code>遵循了<code>DiceGameDelegate</code>协议</p>
|
||||
<pre><code>class DiceGameTracker: DiceGameDelegate {
|
||||
<pre><code class="lang-swift">class DiceGameTracker: DiceGameDelegate {
|
||||
var numberOfTurns = 0
|
||||
func gameDidStart(game: DiceGame) {
|
||||
numberOfTurns = 0
|
||||
@ -836,10 +853,11 @@ protocol DiceGameDelegate {
|
||||
println("The game lasted for \(numberOfTurns) turns")
|
||||
}
|
||||
}
|
||||
</code></pre><p><code>DiceGameTracker</code>实现了<code>DiceGameDelegate</code>协议的方法要求,用来记录游戏已经进行的轮数。 当游戏开始时,<code>numberOfTurns</code>属性被赋值为0;在每新一轮中递加;游戏结束后,输出打印游戏的总轮数。</p>
|
||||
</code></pre>
|
||||
<p><code>DiceGameTracker</code>实现了<code>DiceGameDelegate</code>协议的方法要求,用来记录游戏已经进行的轮数。 当游戏开始时,<code>numberOfTurns</code>属性被赋值为0;在每新一轮中递加;游戏结束后,输出打印游戏的总轮数。</p>
|
||||
<p><code>gameDidStart</code>方法从<code>game</code>参数获取游戏信息并输出。<code>game</code>在方法中被当做<code>DiceGame</code>类型而不是<code>SnakeAndLadders</code>类型,所以方法中只能访问<code>DiceGame</code>协议中的成员。</p>
|
||||
<p><code>DiceGameTracker</code>的运行情况,如下所示:</p>
|
||||
<pre><code>“let tracker = DiceGameTracker()
|
||||
<pre><code class="lang-swift">let tracker = DiceGameTracker()
|
||||
let game = SnakesAndLadders()
|
||||
game.delegate = tracker
|
||||
game.play()
|
||||
@ -849,80 +867,91 @@ game.play()
|
||||
// Rolled a 5
|
||||
// Rolled a 4
|
||||
// Rolled a 5
|
||||
// The game lasted for 4 turns”
|
||||
</code></pre><p><a name="adding_protocol_conformance_with_an_extension"></a></p>
|
||||
// The game lasted for 4 turns
|
||||
</code></pre>
|
||||
<p><a name="adding_protocol_conformance_with_an_extension"></a></p>
|
||||
<h2 id="-">在扩展中添加协议成员</h2>
|
||||
<p>即便无法修改源代码,依然可以通过<code>扩展(Extension)</code>来扩充已存在类型(<em>译者注: 类,结构体,枚举等</em>)。<code>扩展</code>可以为已存在的类型添加<code>属性</code>,<code>方法</code>,<code>下标</code>,<code>协议</code>等成员。详情请在<a href="20_Extensions.html">扩展</a>章节中查看。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>通过<code>扩展</code>为已存在的类型<code>遵循</code>协议时,该类型的所有实例也会随之添加协议中的方法</p>
|
||||
<p>注意:
|
||||
通过<code>扩展</code>为已存在的类型<code>遵循</code>协议时,该类型的所有实例也会随之添加协议中的方法</p>
|
||||
</blockquote>
|
||||
<p><code>TextRepresentable</code>协议含有一个<code>asText</code>,如下所示:</p>
|
||||
<pre><code>protocol TextRepresentable {
|
||||
<pre><code class="lang-swift">protocol TextRepresentable {
|
||||
func asText() -> String
|
||||
}
|
||||
</code></pre><p>通过<code>扩展</code>为上一节中提到的<code>Dice</code>类遵循<code>TextRepresentable</code>协议</p>
|
||||
<pre><code>extension Dice: TextRepresentable {
|
||||
func asText() -> String {
|
||||
</code></pre>
|
||||
<p>通过<code>扩展</code>为上一节中提到的<code>Dice</code>类遵循<code>TextRepresentable</code>协议</p>
|
||||
<pre><code class="lang-swift">extension Dice: TextRepresentable {
|
||||
cun asText() -> String {
|
||||
return "A \(sides)-sided dice"
|
||||
}
|
||||
}
|
||||
</code></pre><p>从现在起,<code>Dice</code>类型的实例可被当作<code>TextRepresentable</code>类型:</p>
|
||||
<pre><code>let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator())
|
||||
</code></pre>
|
||||
<p>从现在起,<code>Dice</code>类型的实例可被当作<code>TextRepresentable</code>类型:</p>
|
||||
<pre><code class="lang-swift">let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator())
|
||||
println(d12.asText())
|
||||
// 输出 "A 12-sided dice"
|
||||
</code></pre><p><code>SnakesAndLadders</code>类也可以通过<code>扩展</code>的方式来遵循协议:</p>
|
||||
<pre><code>extension SnakeAndLadders: TextRepresentable {
|
||||
</code></pre>
|
||||
<p><code>SnakesAndLadders</code>类也可以通过<code>扩展</code>的方式来遵循协议:</p>
|
||||
<pre><code class="lang-swift">extension SnakeAndLadders: TextRepresentable {
|
||||
func asText() -> String {
|
||||
return "A game of Snakes and Ladders with \(finalSquare) squares"
|
||||
}
|
||||
}
|
||||
println(game.asText())
|
||||
// 输出 "A game of Snakes and Ladders with 25 squares"
|
||||
</code></pre><p><a name="declaring_protocol_adoption_with_an_extension"></a></p>
|
||||
<h2 id="-">通过延展补充协议声明</h2>
|
||||
</code></pre>
|
||||
<p><a name="declaring_protocol_adoption_with_an_extension"></a></p>
|
||||
<h2 id="-">通过扩展补充协议声明</h2>
|
||||
<p>当一个类型已经实现了协议中的所有要求,却没有声明时,可以通过<code>扩展</code>来补充协议声明:</p>
|
||||
<pre><code>struct Hamster {
|
||||
<pre><code class="lang-swift">struct Hamster {
|
||||
var name: String
|
||||
func asText() -> String {
|
||||
return "A hamster named \(name)"
|
||||
}
|
||||
}
|
||||
extension Hamster: TextRepresentabl {}
|
||||
</code></pre><p>从现在起,<code>Hamster</code>的实例可以作为<code>TextRepresentable</code>类型使用</p>
|
||||
<pre><code>let simonTheHamster = Hamster(name: "Simon")
|
||||
</code></pre>
|
||||
<p>从现在起,<code>Hamster</code>的实例可以作为<code>TextRepresentable</code>类型使用</p>
|
||||
<pre><code class="lang-swift">let simonTheHamster = Hamster(name: "Simon")
|
||||
let somethingTextRepresentable: TextRepresentabl = simonTheHamester
|
||||
println(somethingTextRepresentable.asText())
|
||||
// 输出 "A hamster named Simon"
|
||||
</code></pre><blockquote>
|
||||
<p>注意:</p>
|
||||
<p>即时满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出明显的协议声明</p>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
即时满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出明显的协议声明</p>
|
||||
</blockquote>
|
||||
<p><a name="collections_of_protocol_types"></a></p>
|
||||
<h2 id="-">集合中的协议类型</h2>
|
||||
<p>协议类型可以被集合使用,表示集合中的元素均为协议类型:</p>
|
||||
<pre><code>let things: TextRepresentable[] = [game,d12,simoTheHamster]
|
||||
</code></pre><p>如下所示,<code>things</code>数组可以被直接遍历,并调用其中元素的<code>asText()</code>函数:</p>
|
||||
<pre><code>for thing in things {
|
||||
<pre><code class="lang-swift">let things: TextRepresentable[] = [game,d12,simoTheHamster]
|
||||
</code></pre>
|
||||
<p>如下所示,<code>things</code>数组可以被直接遍历,并调用其中元素的<code>asText()</code>函数:</p>
|
||||
<pre><code class="lang-swift">for thing in things {
|
||||
println(thing.asText())
|
||||
}
|
||||
// A game of Snakes and Ladders with 25 squares
|
||||
// A 12-sided dice
|
||||
// A hamster named Simon
|
||||
</code></pre><p><code>thing</code>被当做是<code>TextRepresentable</code>类型而不是<code>Dice</code>,<code>DiceGame</code>,<code>Hamster</code>等类型。因此能且仅能调用<code>asText</code>方法</p>
|
||||
</code></pre>
|
||||
<p><code>thing</code>被当做是<code>TextRepresentable</code>类型而不是<code>Dice</code>,<code>DiceGame</code>,<code>Hamster</code>等类型。因此能且仅能调用<code>asText</code>方法</p>
|
||||
<p><a name="protocol_inheritance"></a></p>
|
||||
<h2 id="-">协议的继承</h2>
|
||||
<p>协议能够<em>继承</em>一到多个其他协议。语法与类的继承相似,多个协议间用逗号<code>,</code>分隔</p>
|
||||
<pre><code>protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
|
||||
<pre><code class="lang-swift">protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
|
||||
// 协议定义
|
||||
}
|
||||
</code></pre><p>如下所示,<code>PrettyTextRepresentable</code>协议继承了<code>TextRepresentable</code>协议</p>
|
||||
<pre><code>protocol PrettyTextRepresentable: TextRepresentable {
|
||||
</code></pre>
|
||||
<p>如下所示,<code>PrettyTextRepresentable</code>协议继承了<code>TextRepresentable</code>协议</p>
|
||||
<pre><code class="lang-swift">protocol PrettyTextRepresentable: TextRepresentable {
|
||||
func asPrettyText() -> String
|
||||
}
|
||||
</code></pre><p><code>遵循``PrettyTextRepresentable</code>协议的同时,也需要<code>遵循</code>TextRepresentable`协议。</p>
|
||||
</code></pre>
|
||||
<p><code>遵循``PrettyTextRepresentable</code>协议的同时,也需要<code>遵循</code>TextRepresentable`协议。</p>
|
||||
<p>如下所示,用<code>扩展</code>为<code>SnakesAndLadders</code>遵循<code>PrettyTextRepresentable</code>协议:</p>
|
||||
<pre><code>extension SnakesAndLadders: PrettyTextRepresentable {
|
||||
<pre><code class="lang-swift">extension SnakesAndLadders: PrettyTextRepresentable {
|
||||
func asPrettyText() -> String {
|
||||
var output = asText() + ":\n"
|
||||
for index in 1...finalSquare {
|
||||
@ -938,21 +967,23 @@ println(somethingTextRepresentable.asText())
|
||||
return output
|
||||
}
|
||||
}
|
||||
</code></pre><p>在<code>for in</code>中迭代出了<code>board</code>数组中的每一个元素:</p>
|
||||
</code></pre>
|
||||
<p>在<code>for in</code>中迭代出了<code>board</code>数组中的每一个元素:</p>
|
||||
<ul>
|
||||
<li>当从数组中迭代出的元素的值大于0时,用<code>▲</code>表示</li>
|
||||
<li>当从数组中迭代出的元素的值小于0时,用<code>▼</code>表示</li>
|
||||
<li>当从数组中迭代出的元素的值等于0时,用<code>○</code>表示</li>
|
||||
</ul>
|
||||
<p>任意<code>SankesAndLadders</code>的实例都可以使用<code>asPrettyText()</code>方法。</p>
|
||||
<pre><code>println(game.asPrettyText())
|
||||
<pre><code class="lang-swift">println(game.asPrettyText())
|
||||
// A game of Snakes and Ladders with 25 squares:
|
||||
// ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○
|
||||
</code></pre><p><a name="protocol_composition"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="protocol_composition"></a></p>
|
||||
<h2 id="-">协议合成</h2>
|
||||
<p>一个协议可由多个协议采用<code>protocol<SomeProtocol, AnotherProtocol></code>这样的格式进行组合,称为<code>协议合成(protocol composition)</code>。</p>
|
||||
<p>举个例子:</p>
|
||||
<pre><code>protocol Named {
|
||||
<pre><code class="lang-swift">protocol Named {
|
||||
var name: String { get }
|
||||
}
|
||||
protocol Aged {
|
||||
@ -968,11 +999,12 @@ func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
|
||||
let birthdayPerson = Person(name: "Malcolm", age: 21)
|
||||
wishHappyBirthday(birthdayPerson)
|
||||
// 输出 "Happy birthday Malcolm - you're 21!
|
||||
</code></pre><p><code>Named</code>协议包含<code>String</code>类型的<code>name</code>属性;<code>Aged</code>协议包含<code>Int</code>类型的<code>age</code>属性。<code>Person</code>结构体<code>遵循</code>了这两个协议。</p>
|
||||
</code></pre>
|
||||
<p><code>Named</code>协议包含<code>String</code>类型的<code>name</code>属性;<code>Aged</code>协议包含<code>Int</code>类型的<code>age</code>属性。<code>Person</code>结构体<code>遵循</code>了这两个协议。</p>
|
||||
<p><code>wishHappyBirthday</code>函数的形参<code>celebrator</code>的类型为<code>protocol<Named,Aged></code>。可以传入任意<code>遵循</code>这两个协议的类型的实例</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p><code>协议合成</code>并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。</p>
|
||||
<p>注意:
|
||||
<code>协议合成</code>并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。</p>
|
||||
</blockquote>
|
||||
<p><a name="checking_for_protocol_conformance"></a></p>
|
||||
<h2 id="-">检验协议的一致性</h2>
|
||||
@ -982,14 +1014,15 @@ wishHappyBirthday(birthdayPerson)
|
||||
<li><code>as?</code>返回一个可选值,当实例<code>遵循</code>协议时,返回该协议类型;否则返回<code>nil</code></li>
|
||||
<li><code>as</code>用以强制向下转换型。</li>
|
||||
</ul>
|
||||
<pre><code>@objc protocol HasArea {
|
||||
<pre><code class="lang-swift">@objc protocol HasArea {
|
||||
var area: Double { get }
|
||||
}
|
||||
</code></pre><blockquote>
|
||||
<p>注意:</p>
|
||||
<p><code>@objc</code>用来表示协议是可选的,也可以用来表示暴露给<code>Objective-C</code>的代码,此外,<code>@objc</code>型协议只对<code>类</code>有效,因此只能在<code>类</code>中检查协议的一致性。详情查看<em><a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216" target="_blank">Using Siwft with Cocoa and Objectivei-c</a></em>。</p>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
<code>@objc</code>用来表示协议是可选的,也可以用来表示暴露给<code>Objective-C</code>的代码,此外,<code>@objc</code>型协议只对<code>类</code>有效,因此只能在<code>类</code>中检查协议的一致性。详情查看<em><a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216" target="_blank">Using Siwft with Cocoa and Objectivei-c</a></em>。</p>
|
||||
</blockquote>
|
||||
<pre><code>class Circle: HasArea {
|
||||
<pre><code class="lang-swift">class Circle: HasArea {
|
||||
let pi = 3.1415927
|
||||
var radius: Double
|
||||
var area:≈radius }
|
||||
@ -999,20 +1032,23 @@ class Country: HasArea {
|
||||
var area: Double
|
||||
init(area: Double) { self.area = area }
|
||||
}
|
||||
</code></pre><p><code>Circle</code>和<code>Country</code>都遵循了<code>HasArea</code>协议,前者把<code>area</code>写为计算型属性(computed property),后者则把<code>area</code>写为存储型属性(stored property)。</p>
|
||||
</code></pre>
|
||||
<p><code>Circle</code>和<code>Country</code>都遵循了<code>HasArea</code>协议,前者把<code>area</code>写为计算型属性(computed property),后者则把<code>area</code>写为存储型属性(stored property)。</p>
|
||||
<p>如下所示,<code>Animal</code>类没有实现任何协议</p>
|
||||
<pre><code>class Animal {
|
||||
<pre><code class="lang-swift">class Animal {
|
||||
var legs: Int
|
||||
init(legs: Int) { self.legs = legs }
|
||||
}
|
||||
</code></pre><p><code>Circle,Country,Animal</code>并没有一个相同的基类,所以采用<code>AnyObject</code>类型的数组来装载在它们的实例,如下所示:</p>
|
||||
<pre><code>let objects: AnyObject[] = [
|
||||
</code></pre>
|
||||
<p><code>Circle,Country,Animal</code>并没有一个相同的基类,所以采用<code>AnyObject</code>类型的数组来装载在它们的实例,如下所示:</p>
|
||||
<pre><code class="lang-swift">let objects: AnyObject[] = [
|
||||
Circle(radius: 2.0),
|
||||
Country(area: 243_610),
|
||||
Animal(legs: 4)
|
||||
]
|
||||
</code></pre><p>如下所示,在迭代时检查<code>object</code>数组的元素是否<code>遵循</code>了<code>HasArea</code>协议:</p>
|
||||
<pre><code>for object in objects {
|
||||
</code></pre>
|
||||
<p>如下所示,在迭代时检查<code>object</code>数组的元素是否<code>遵循</code>了<code>HasArea</code>协议:</p>
|
||||
<pre><code class="lang-swift">for object in objects {
|
||||
if let objectWithArea = object as? HasArea {
|
||||
println("Area is \(objectWithArea.area)")
|
||||
} else {
|
||||
@ -1022,7 +1058,8 @@ class Country: HasArea {
|
||||
// Area is 12.5663708
|
||||
// Area is 243610.0
|
||||
// Something that doesn't have an area
|
||||
</code></pre><p>当数组中的元素遵循<code>HasArea</code>协议时,通过<code>as?</code>操作符将其<code>可选绑定(optional binding)</code>到<code>objectWithArea</code>常量上。</p>
|
||||
</code></pre>
|
||||
<p>当数组中的元素遵循<code>HasArea</code>协议时,通过<code>as?</code>操作符将其<code>可选绑定(optional binding)</code>到<code>objectWithArea</code>常量上。</p>
|
||||
<p><code>objects</code>数组中元素的类型并不会因为<code>向下转型</code>而改变,当它们被赋值给<code>objectWithArea</code>时只被视为<code>HasArea</code>类型,因此只有<code>area</code>属性能够被访问。</p>
|
||||
<p><a name="optional_protocol_requirements"></a></p>
|
||||
<h2 id="-">可选协议要求</h2>
|
||||
@ -1030,21 +1067,22 @@ class Country: HasArea {
|
||||
<p>可选协议在调用时使用<code>可选链</code>,详细内容在<a href="17_Optional_Chaining.html">可选链</a>章节中查看。</p>
|
||||
<p>像<code>someOptionalMethod?(someArgument)</code>一样,你可以在可选方法名称后加上<code>?</code>来检查该方法是否被实现。<code>可选方法</code>和<code>可选属性</code>都会返回一个<code>可选值(optional value)</code>,当其不可访问时,<code>?</code>之后语句不会执行,并返回<code>nil</code>。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>可选协议只能在含有<code>@objc</code>前缀的协议中生效。且<code>@objc</code>的协议只能被<code>类</code>遵循。</p>
|
||||
<p>注意:
|
||||
可选协议只能在含有<code>@objc</code>前缀的协议中生效。且<code>@objc</code>的协议只能被<code>类</code>遵循。</p>
|
||||
</blockquote>
|
||||
<p><code>Counter</code>类使用<code>CounterDataSource</code>类型的外部数据源来提供<code>增量值(increment amount)</code>,如下所示:</p>
|
||||
<pre><code>@objc protocol CounterDataSource {
|
||||
<pre><code class="lang-swift">@objc protocol CounterDataSource {
|
||||
@optional func incrementForCount(count: Int) -> Int
|
||||
@optional var fixedIncrement: Int { get }
|
||||
}
|
||||
</code></pre><p><code>CounterDataSource</code>含有<code>incrementForCount</code>的<code>可选方法</code>和<code>fiexdIncrement</code>的<code>可选属性</code>。</p>
|
||||
</code></pre>
|
||||
<p><code>CounterDataSource</code>含有<code>incrementForCount</code>的<code>可选方法</code>和<code>fiexdIncrement</code>的<code>可选属性</code>。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p><code>CounterDataSource</code>中的属性和方法都是可选的,因此可以在类中声明但不实现这些成员,尽管技术上允许这样做,不过最好不要这样写。</p>
|
||||
<p>注意:
|
||||
<code>CounterDataSource</code>中的属性和方法都是可选的,因此可以在类中声明但不实现这些成员,尽管技术上允许这样做,不过最好不要这样写。</p>
|
||||
</blockquote>
|
||||
<p><code>Counter</code>类含有<code>CounterDataSource?</code>类型的可选属性<code>dataSource</code>,如下所示:</p>
|
||||
<pre><code>@objc class Counter {
|
||||
<pre><code class="lang-swift">@objc class Counter {
|
||||
var count = 0
|
||||
var dataSource: CounterDataSource?
|
||||
func increment() {
|
||||
@ -1055,7 +1093,8 @@ class Country: HasArea {
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre><p><code>count</code>属性用于存储当前的值,<code>increment</code>方法用来为<code>count</code>赋值。</p>
|
||||
</code></pre>
|
||||
<p><code>count</code>属性用于存储当前的值,<code>increment</code>方法用来为<code>count</code>赋值。</p>
|
||||
<p><code>increment</code>方法通过<code>可选链</code>,尝试从两种<code>可选成员</code>中获取<code>count</code>。</p>
|
||||
<ol>
|
||||
<li>由于<code>dataSource</code>可能为<code>nil</code>,因此在<code>dataSource</code>后边加上了<code>?</code>标记来表明只在<code>dataSource</code>非空时才去调用incrementForCount`方法。</li>
|
||||
@ -1064,11 +1103,12 @@ class Country: HasArea {
|
||||
<p>在调用<code>incrementForCount</code>方法后,<code>Int</code>型<code>可选值</code>通过<code>可选绑定(optional binding)</code>自动拆包并赋值给常量<code>amount</code>。</p>
|
||||
<p>当<code>incrementForCount</code>不能被调用时,尝试使用<code>可选属性``fixedIncrement</code>来代替。</p>
|
||||
<p><code>ThreeSource</code>实现了<code>CounterDataSource</code>协议,如下所示:</p>
|
||||
<pre><code>class ThreeSource: CounterDataSource {
|
||||
<pre><code class="lang-swift">class ThreeSource: CounterDataSource {
|
||||
let fixedIncrement = 3
|
||||
}
|
||||
</code></pre><p>使用<code>ThreeSource</code>作为数据源开实例化一个<code>Counter</code>:</p>
|
||||
<pre><code>var counter = Counter()
|
||||
</code></pre>
|
||||
<p>使用<code>ThreeSource</code>作为数据源开实例化一个<code>Counter</code>:</p>
|
||||
<pre><code class="lang-swift">var counter = Counter()
|
||||
counter.dataSource = ThreeSource()
|
||||
for _ in 1...4 {
|
||||
counter.increment()
|
||||
@ -1078,8 +1118,9 @@ for _ in 1...4 {
|
||||
// 6
|
||||
// 9
|
||||
// 12
|
||||
</code></pre><p><code>TowardsZeroSource</code>实现了<code>CounterDataSource</code>协议中的<code>incrementForCount</code>方法,如下所示:</p>
|
||||
<pre><code>class TowardsZeroSource: CounterDataSource {
|
||||
</code></pre>
|
||||
<p><code>TowardsZeroSource</code>实现了<code>CounterDataSource</code>协议中的<code>incrementForCount</code>方法,如下所示:</p>
|
||||
<pre><code class="lang-swift">class TowardsZeroSource: CounterDataSource {
|
||||
func incrementForCount(count: Int) -> Int {
|
||||
if count == 0 {
|
||||
return 0
|
||||
@ -1090,8 +1131,9 @@ func incrementForCount(count: Int) -> Int {
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre><p>下边是执行的代码:</p>
|
||||
<pre><code>counter.count = -4
|
||||
</code></pre>
|
||||
<p>下边是执行的代码:</p>
|
||||
<pre><code class="lang-swift">counter.count = -4
|
||||
counter.dataSource = TowardsZeroSource()
|
||||
for _ in 1...5 {
|
||||
counter.increment()
|
||||
@ -1103,6 +1145,7 @@ for _ in 1...5 {
|
||||
// 0
|
||||
// 0
|
||||
</code></pre>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user