Swift3.0官方文档与中文翻译对照

Protocols (协议上)

2017-01-21  本文已影响22人  金旭峰

Aprotocoldefines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then beadoptedby a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said toconformto that protocol.

协议定义了一个蓝图,规定了用来实现某一特定任务或者功能的方法、属性,以及其他需要的东西。类、结构体或枚举都可以遵循协议,并为协议定义的这些要求提供具体实现。某个类型能够满足某个协议的要求,就可以说该类型遵循这个协议。

In addition to specifying requirements that conforming types must implement, you can extend a protocol to implement some of these requirements or to implement additional functionality that conforming types can take advantage of.

除了遵循协议的类型必须实现的要求外,还可以对协议进行扩展,通过扩展来实现一部分要求或者实现一些附加功能,这样遵循协议的类型就能够使用这些功能。

Protocol Syntax (协议语法)

You define protocols in a very similar way to classes, structures, and enumerations:

协议的定义方式与类、结构体和枚举的定义非常相似:

protocol SomeProtocol{

// protocol definition goes here

}

Custom types state that they adopt a particular protocol by placing the protocol’s name after the type’s name, separated by a colon, as part of their definition. Multiple protocols can be listed, and are separated by commas:

要让自定义类型遵循某个协议,在定义类型时,需要在类型名称后加上协议名称,中间以冒号(:)分隔。遵循多个协议时,各协议之间用逗号(,)分隔:

struct SomeStructure:FirstProtocol,AnotherProtocol{

// structure definition goes here

}

If a class has a superclass, list the superclass name before any protocols it adopts, followed by a comma:

拥有父类的类在遵循协议时,应该将父类名放在协议名之前,以逗号分隔:

class SomeClass:SomeSuperclass, FirstProtocol, AnotherProtocol{

// class definition goes here

}

Property Requirements (属性要求)

A protocol can require any conforming type to provide an instance property or type property with a particular name and type. The protocol doesn’t specify whether the property should be a stored property or a computed property—it only specifies the required property name and type. The protocol also specifies whether each property must be gettable or gettableandsettable.

协议可以要求遵循协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型属性,它只指定属性的名称和类型。此外,协议还指定属性是可读的还是可读可写的。

If a protocol requires a property to be gettable and settable, that property requirement cannot be fulfilled by a constant stored property or a read-only computed property. If the protocol only requires a property to be gettable, the requirement can be satisfied by any kind of property, and it is valid for the property to be also settable if this is useful for your own code.

如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是可读的,那么该属性不仅可以是可读的,如果代码需要的话,还可以是可写的。

Property requirements are always declared as variable properties, prefixed with thevarkeyword. Gettable and settable properties are indicated by writing{ get set }after their type declaration, and gettable properties are indicated by writing{ get }.

协议总是用var关键字来声明变量属性,在类型声明后加上{ set get }来表示属性是可读可写的,可读属性则用{ get }来表示:

protocol SomeProtocol{

    var mustBeSettable:Int{getset}

    var doesNotNeedToBeSettable:Int{get}

}

Always prefix type property requirements with thestatickeyword when you define them in a protocol. This rule pertains even though type property requirements can be prefixed with theclassorstatickeyword when implemented by a class:

在协议中定义类型属性时,总是使用static关键字作为前缀。当类类型遵循协议时,除了static关键字,还可以使用class关键字来声明类型属性:

protocol AnotherProtocol{

    static var someTypeProperty:Int{getset}

}

Here’s an example of a protocol with a single instance property requirement:

如下所示,这是一个只含有一个实例属性要求的协议:

protocol FullyNamed{

    var fullName:String{get}

}

TheFullyNamedprotocol requires a conforming type to provide a fully-qualified name. The protocol doesn’t specify anything else about the nature of the conforming type—it only specifies that the type must be able to provide a full name for itself. The protocol states that anyFullyNamedtype must have a gettable instance property calledfullName, which is of typeString.

FullyNamed协议除了要求遵循协议的类型提供fullName属性外,并没有其他特别的要求。这个协议表示,任何遵循FullyNamed的类型,都必须有一个可读的String类型的实例属性fullName。

Here’s an example of a simple structure that adopts and conforms to theFullyNamedprotocol:

下面是一个遵循FullyNamed协议的简单结构体:

struct Person:FullyNamed{

    var fullName:String

}

let john=Person(fullName:"John Appleseed")

// john.fullName is "John Appleseed"

This example defines a structure calledPerson, which represents a specific named person. It states that it adopts theFullyNamedprotocol as part of the first line of its definition.

这个例子中定义了一个叫做Person的结构体,用来表示一个具有名字的人。从第一行代码可以看出,它遵循了FullyNamed协议。

Each instance ofPersonhas a single stored property calledfullName, which is of typeString. This matches the single requirement of theFullyNamedprotocol, and means thatPersonhas correctly conformed to the protocol. (Swift reports an error at compile-time if a protocol requirement is not fulfilled.)

Person结构体的每一个实例都有一个String类型的存储型属性fullName。这正好满足了FullyNamed协议的要求,也就意味着Person结构体正确地符合了协议。(如果协议要求未被完全满足,在编译时会报错。)

Here’s a more complex class, which also adopts and conforms to theFullyNamedprotocol:

下面是一个更为复杂的类,它适配并遵循了FullyNamed协议:

class Starship:FullyNamed{

var prefix:String?

var name:String

init(name:String,prefix:String? =nil) {

    self.name=name

    self.prefix=prefix

}

var fullName:String{

return (prefix!=nil?prefix! +" ":"") +name

  }

}

var ncc1701=Starship(name:"Enterprise",prefix:"USS")

// ncc1701.fullName is "USS Enterprise"

This class implements thefullNameproperty requirement as a computed read-only property for a starship. EachStarshipclass instance stores a mandatorynameand an optionalprefix. ThefullNameproperty uses theprefixvalue if it exists, and prepends it to the beginning ofnameto create a full name for the starship.

Starship类把fullName属性实现为只读的计算型属性。每一个Starship类的实例都有一个名为name的非可选属性和一个名为prefix的可选属性。 当prefix存在时,计算型属性fullName会将prefix插入到name之前,从而为星际飞船构建一个全名。

Method Requirements (方法要求)

Protocols can require specific instance methods and type methods to be implemented by conforming types. These methods are written as part of the protocol’s definition in exactly the same way as for normal instance and type methods, but without curly braces or a method body. Variadic parameters are allowed, subject to the same rules as for normal methods. Default values, however, cannot be specified for method parameters within a protocol’s definition.

协议可以要求遵循协议的类型实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是,不支持为协议中的方法的参数提供默认值。

As with type property requirements, you always prefix type method requirements with thestatickeyword when they are defined in a protocol. This is true even though type method requirements are prefixed with theclassorstatickeyword when implemented by a class:

正如属性要求中所述,在协议中定义类方法的时候,总是使用static关键字作为前缀。当类类型遵循协议时,除了static关键字,还可以使用class关键字作为前缀:

protocol SomeProtocol{

    static func someTypeMethod()

}

The following example defines a protocol with a single instance method requirement:

下面的例子定义了一个只含有一个实例方法的协议:

protocol RandomNumberGenerator{

    func random() ->Double

}

This protocol,RandomNumberGenerator, requires any conforming type to have an instance method calledrandom, which returns aDoublevalue whenever it is called. Although it is not specified as part of the protocol, it is assumed that this value will be a number from0.0up to (but not including)1.0.

RandomNumberGenerator协议要求遵循协议的类型必须拥有一个名为random, 返回值类型为Double的实例方法。尽管这里并未指明,但是我们假设返回值在[0.0,1.0)区间内。

TheRandomNumberGeneratorprotocol does not make any assumptions about how each random number will be generated—it simply requires the generator to provide a standard way to generate a new random number.

RandomNumberGenerator协议并不关心每一个随机数是怎样生成的,它只要求必须提供一个随机数生成器。

Here’s an implementation of a class that adopts and conforms to theRandomNumberGeneratorprotocol. This class implements a pseudorandom number generator algorithm known as alinear congruential generator:

如下所示,下边是一个遵循并符合RandomNumberGenerator协议的类。该类实现了一个叫做线性同余生成器(linear congruential generator)的伪随机数算法。

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).truncatingRemainder(dividingBy:m))

return lastRandom/m

}

}

let generator=LinearCongruentialGenerator()

print("Here's a random number:\(generator.random())")

// Prints "Here's a random number: 0.37464991998171"

print("And another one:\(generator.random())")

// Prints "And another one: 0.729023776863283"

Mutating Method Requirements (Mutating 方法要求)

It is sometimes necessary for a method to modify (ormutate) the instance it belongs to. For instance methods on value types (that is, structures and enumerations) you place themutatingkeyword before a method’sfunckeyword to indicate that the method is allowed to modify the instance it belongs to and any properties of that instance. This process is described inModifying Value Types from Within Instance Methods.

有时需要在方法中改变方法所属的实例。例如,在值类型(即结构体和枚举)的实例方法中,将mutating关键字作为方法的前缀,写在func关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。这一过程在在实例方法中修改值类型章节中有详细描述。

If you define a protocol instance method requirement that is intended to mutate instances of any type that adopts the protocol, mark the method with themutatingkeyword as part of the protocol’s definition. This enables structures and enumerations to adopt the protocol and satisfy that method requirement.

如果你在协议中定义了一个实例方法,该方法会改变遵循该协议的类型的实例,那么在定义协议时需要在方法前加mutating关键字。这使得结构体和枚举能够遵循此协议并满足此方法要求。

NOTE

If you mark a protocol instance method requirement asmutating, you do not need to write themutatingkeyword when writing an implementation of that method for a class. Themutatingkeyword is only used by structures and enumerations.

实现协议中的mutating方法时,若是类类型,则不用写mutating关键字。而对于结构体和枚举,则必须写mutating关键字。

The example below defines a protocol calledTogglable, which defines a single instance method requirement calledtoggle. As its name suggests, thetoggle()method is intended to toggle or invert the state of any conforming type, typically by modifying a property of that type.

如下所示,Togglable协议只要求实现一个名为toggle的实例方法。根据名称的暗示,toggle()方法将改变实例属性,从而切换遵循该协议类型的实例的状态。

Thetoggle()method is marked with themutatingkeyword as part of theTogglableprotocol definition, to indicate that the method is expected to mutate the state of a conforming instance when it is called:

toggle()方法在定义的时候,使用mutating关键字标记,这表明当它被调用时,该方法将会改变遵循协议的类型的实例:

protocol Togglable{

    mutating functoggle()

}

If you implement theTogglableprotocol for a structure or enumeration, that structure or enumeration can conform to the protocol by providing an implementation of thetoggle()method that is also marked asmutating.

当使用枚举或结构体来实现Togglable协议时,需要提供一个带有mutating前缀的toggle()方法。

The example below defines an enumeration calledOnOffSwitch. This enumeration toggles between two states, indicated by the enumeration casesonandoff. The enumeration’stoggleimplementation is marked asmutating, to match theTogglableprotocol’s requirements:

下面定义了一个名为OnOffSwitch的枚举。这个枚举在两种状态之间进行切换,用枚举成员On和Off表示。枚举的toggle()方法被标记为mutating,以满足Togglable协议的要求:

enum OnOffSwitch:Togglable{

case off,on

mutating func toggle() {

switch self{

case.off:

   self= .on

case.on:

   self= .off

}

}

}

var lightSwitch=OnOffSwitch.off

lightSwitch.toggle()

// lightSwitch is now equal to .on

Initializer Requirements (构造器要求)

Protocols can require specific initializers to be implemented by conforming types. You write these initializers as part of the protocol’s definition in exactly the same way as for normal initializers, but without curly braces or an initializer body:

协议可以要求遵循协议的类型实现指定的构造器。你可以像编写普通构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体:

protocol SomeProtocol{

    init(someParameter:Int)

}

Class Implementations of Protocol Initializer Requirements (构造器要求在类中的实现)

You can implement a protocol initializer requirement on a conforming class as either a designated initializer or a convenience initializer. In both cases, you must mark the initializer implementation with therequiredmodifier:

你可以在遵循协议的类中实现构造器,无论是作为指定构造器,还是作为便利构造器。无论哪种情况,你都必须为构造器实现标上required修饰符:

class SomeClass:SomeProtocol{

    required init(someParameter:Int) {

        // initializer implementation goes here

    }

}

The use of therequiredmodifier ensures that you provide an explicit or inherited implementation of the initializer requirement on all subclasses of the conforming class, such that they also conform to the protocol.

使用required修饰符可以确保所有子类也必须提供此构造器实现,从而也能符合协议。

For more information on required initializers, seeRequired Initializers.

关于required构造器的更多内容,请参考必要构造器

NOTE

You do not need to mark protocol initializer implementations with therequiredmodifier on classes that are marked with thefinalmodifier, because final classes cannot be subclassed. For more on thefinalmodifier, seePreventing Overrides.

如果类已经被标记为final,那么不需要在协议构造器的实现中使用required修饰符,因为final类不能有子类。关于final修饰符的更多内容,请参见防止重写

If a subclass overrides a designated initializer from a superclass, and also implements a matching initializer requirement from a protocol, mark the initializer implementation with both therequiredandoverridemodifiers:

如果一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标注required和override修饰符:

protocol SomeProtocol{

    init()

}

class SomeSuperClass{

    init() {

        // initializer implementation goes here

    }

}

class SomeSubClass:SomeSuperClass,SomeProtocol{

    // "required" from SomeProtocol conformance; "override" from SomeSuperClass

    requiredoverrideinit() {

        // initializer implementation goes here

    }

}

Failable Initializer Requirements (可失败构造器要求)

Protocols can define failable initializer requirements for conforming types, as defined inFailable Initializers.

协议还可以为遵循协议的类型定义可失败构造器要求,详见可失败构造器

A failable initializer requirement can be satisfied by a failable or nonfailable initializer on a conforming type. A nonfailable initializer requirement can be satisfied by a nonfailable initializer or an implicitly unwrapped failable initializer.

遵循协议的类型可以通过可失败构造器(init?)或非可失败构造器(init)来满足协议中定义的可失败构造器要求。协议中定义的非可失败构造器要求可以通过非可失败构造器(init)或隐式解包可失败构造器(init!)来满足。

Protocols as Types (协议作为类型)

Protocols do not actually implement any functionality themselves. Nonetheless, any protocol you create will become a fully-fledged type for use in your code.

尽管协议本身并未实现任何功能,但是协议可以被当做一个成熟的类型来使用。

Because it is a type, you can use a protocol in many places where other types are allowed, including:

协议可以像其他普通类型一样使用,使用场景如下:

1. As a parameter type or return type in a function, method, or initializer

作为函数、方法或构造器中的参数类型或返回值类型

2. As the type of a constant, variable, or property

作为常量、变量或属性的类型

3. As the type of items in an array, dictionary, or other container

作为数组、字典或其他容器中的元素类型

NOTE

Because protocols are types, begin their names with a capital letter (such asFullyNamedandRandomNumberGenerator) to match the names of other types in Swift (such asInt,String, andDouble).

协议是一种类型,因此协议类型的名称应与其他类型(例如Int,Double,String)的写法相同,使用大写字母开头的驼峰式写法,例如(FullyNamed和RandomNumberGenerator)。

Here’s an example of a protocol used as a type:

下面是将协议作为类型使用的例子:

class Dice{

    let sides:Int

    let generator:RandomNumberGenerator

    init(sides:Int,generator:RandomNumberGenerator) {

        self.sides=sides

        self.generator=generator

    }

    func roll() ->Int{

        return Int(generator.random() *Double(sides)) +1

    }

}

This example defines a new class calledDice, which represents ann-sided dice for use in a board game.Diceinstances have an integer property calledsides, which represents how many sides they have, and a property calledgenerator, which provides a random number generator from which to create dice roll values.

例子中定义了一个Dice类,用来代表桌游中拥有 N 个面的骰子。Dice的实例含有sides和generator两个属性,前者是整型,用来表示骰子有几个面,后者为骰子提供一个随机数生成器,从而生成随机点数。

Thegeneratorproperty is of typeRandomNumberGenerator. Therefore, you can set it to an instance ofanytype that adopts theRandomNumberGeneratorprotocol. Nothing else is required of the instance you assign to this property, except that the instance must adopt theRandomNumberGeneratorprotocol.

generator属性的类型为RandomNumberGenerator,因此任何遵循了RandomNumberGenerator协议的类型的实例都可以赋值给generator,除此之外并无其他要求。

Dicealso has an initializer, to set up its initial state. This initializer has a parameter calledgenerator, which is also of typeRandomNumberGenerator. You can pass a value of any conforming type in to this parameter when initializing a newDiceinstance.

Dice类还有一个构造器,用来设置初始状态。构造器有一个名为generator,类型为RandomNumberGenerator的形参。在调用构造方法创建Dice的实例时,可以传入任何遵循RandomNumberGenerator协议的实例给generator。

Diceprovides one instance method,roll, which returns an integer value between 1 and the number of sides on the dice. This method calls the generator’srandom()method to create a new random number between0.0and1.0, and uses this random number to create a dice roll value within the correct range. Becausegeneratoris known to adoptRandomNumberGenerator, it is guaranteed to have arandom()method to call.

Dice类提供了一个名为roll的实例方法,用来模拟骰子的面值。它先调用generator的random()方法来生成一个[0.0,1.0)区间内的随机数,然后使用这个随机数生成正确的骰子面值。因为generator遵循了RandomNumberGenerator协议,可以确保它有个random()方法可供调用。

Here’s how theDiceclass can be used to create a six-sided dice with aLinearCongruentialGeneratorinstance as its random number generator:

下面的例子展示了如何使用LinearCongruentialGenerator的实例作为随机数生成器来创建一个六面骰子:

var d6=Dice(sides:6,generator:LinearCongruentialGenerator())

    for_in1...5{

        print("Random dice roll is\(d6.roll())")   

    }

// Random dice roll is 3

// Random dice roll is 5

// Random dice roll is 4

// Random dice roll is 5

// Random dice roll is 4

Delegation (委托(代理)模式)

Delegationis a design pattern that enables a class or structure to hand off (ordelegate) some of its responsibilities to an instance of another type. This design pattern is implemented by defining a protocol that encapsulates the delegated responsibilities, such that a conforming type (known as a delegate) is guaranteed to provide the functionality that has been delegated. Delegation can be used to respond to a particular action, or to retrieve data from an external source without needing to know the underlying type of that source.

委托是一种设计模式,它允许类或结构体将一些需要它们负责的功能委托给其他类型的实例。委托模式的实现很简单:定义协议来封装那些需要被委托的功能,这样就能确保遵循协议的类型能提供这些功能。委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型。

The example below defines two protocols for use with dice-based board games:

下面的例子定义了两个基于骰子游戏的协议:

protocol DiceGame{

    var dice:Dice{get}

    func play()

}

protocol DiceGameDelegate{

func gameDidStart(_game:DiceGame)

func game(_game:DiceGame,didStartNewTurnWithDiceRolldiceRoll:Int)

func gameDidEnd(_game:DiceGame)

}

TheDiceGameprotocol is a protocol that can be adopted by any game that involves dice. TheDiceGameDelegateprotocol can be adopted by any type to track the progress of aDiceGame.

DiceGame协议可以被任意涉及骰子的游戏遵循。DiceGameDelegate协议可以被任意类型遵循,用来追踪DiceGame的游戏过程。

Here’s a version of theSnakes and Laddersgame originally introduced inControl Flow. This version is adapted to use aDiceinstance for its dice-rolls; to adopt theDiceGameprotocol; and to notify aDiceGameDelegateabout its progress:

如下所示,SnakesAndLadders是控制流章节引入的蛇梯棋游戏的新版本。新版本使用Dice实例作为骰子,并且实现了DiceGame和DiceGameDelegate协议,后者用来记录游戏的过程:

class SnakesAndLadders:DiceGame{

  let finalSquare=25

  let dice=Dice(sides:6,generator:LinearCongruentialGenerator())

  var square=0

  var board: [Int]

  init() {

    board=Array(repeating:0,count:finalSquare+1)

    board[03] = +08;board[06] = +11;board[09] = +09;board[10] = +02

    board[14] =-10;board[19] =-11;board[22] =-02;board[24] =-08

  }

  var delegate:DiceGameDelegate?

  func play() {

  square=0

  delegate?.gameDidStart(self)

  gameLoop:whilesquare!=finalSquare{

    let diceRoll=dice.roll()

    delegate?.game(self,didStartNewTurnWithDiceRoll:diceRoll)

    switch square+diceRoll{

      case finalSquare:

        breakgameLoop

      case let newSquarewherenewSquare>finalSquare:

        continuegameLoop

      default:

        square+=diceRoll

        square+=board[square]

    }

}

delegate?.gameDidEnd(self)

}

}

For a description of theSnakes and Laddersgameplay, seeBreaksection of theControl Flow.

关于这个蛇梯棋游戏的详细描述请参阅控制流章节中的Break部分。

This version of the game is wrapped up as a class calledSnakesAndLadders, which adopts theDiceGameprotocol. It provides a gettablediceproperty and aplay()method in order to conform to the protocol. (Thediceproperty is declared as a constant property because it does not need to change after initialization, and the protocol only requires that it is gettable.)

这个版本的游戏封装到了SnakesAndLadders类中,该类遵循了DiceGame协议,并且提供了相应的可读的dice属性和play()方法。(dice属性在构造之后就不再改变,且协议只要求dice为可读的,因此将dice声明为常量属性。)

TheSnakes and Laddersgame board setup takes place within the class’sinit()initializer. All game logic is moved into the protocol’splaymethod, which uses the protocol’s requireddiceproperty to provide its dice roll values.

游戏使用SnakesAndLadders类的init()构造器来初始化游戏。所有的游戏逻辑被转移到了协议中的play()方法,play()方法使用协议要求的dice属性提供骰子摇出的值。

Note that thedelegateproperty is defined as anoptionalDiceGameDelegate, because a delegate isn’t required in order to play the game. Because it is of an optional type, thedelegateproperty is automatically set to an initial value ofnil. Thereafter, the game instantiator has the option to set the property to a suitable delegate.

注意,delegate并不是游戏的必备条件,因此delegate被定义为DiceGameDelegate类型的可选属性。因为delegate是可选值,因此会被自动赋予初始值nil。随后,可以在游戏中为delegate设置适当的值。

DiceGameDelegateprovides three methods for tracking the progress of a game. These three methods have been incorporated into the game logic within theplay()method above, and are called when a new game starts, a new turn begins, or the game ends.

DicegameDelegate协议提供了三个方法用来追踪游戏过程。这三个方法被放置于游戏的逻辑中,即play()方法内。分别在游戏开始时,新一轮开始时,以及游戏结束时被调用。

Because thedelegateproperty is anoptionalDiceGameDelegate, theplay()method uses optional chaining each time it calls a method on the delegate. If thedelegateproperty is nil, these delegate calls fail gracefully and without error. If thedelegateproperty is non-nil, the delegate methods are called, and are passed theSnakesAndLaddersinstance as a parameter.

因为delegate是一个DiceGameDelegate类型的可选属性,因此在play()方法中通过可选链式调用来调用它的方法。若delegate属性为nil,则调用方法会优雅地失败,并不会产生错误。若delegate不为nil,则方法能够被调用,并传递SnakesAndLadders实例作为参数。

This next example shows a class calledDiceGameTracker, which adopts theDiceGameDelegateprotocol:

如下示例定义了DiceGameTracker类,它遵循了DiceGameDelegate协议:

class DiceGameTracker:DiceGameDelegate{

  var numberOfTurns=0

  func gameDidStart(_game:DiceGame) {

    numberOfTurns=0

    if gameisSnakesAndLadders{

    print("Started a new game of Snakes and Ladders")

  }

    print("The game is using a\(game.dice.sides)-sided dice")

}

func game(_game:DiceGame,didStartNewTurnWithDiceRolldiceRoll:Int) {

    numberOfTurns+=1

    print("Rolled a\(diceRoll)")

}

  func gameDidEnd(_game:DiceGame) {

    print("The game lasted for\(numberOfTurns)turns")

  }

}

DiceGameTrackerimplements all three methods required byDiceGameDelegate. It uses these methods to keep track of the number of turns a game has taken. It resets anumberOfTurnsproperty to zero when the game starts, increments it each time a new turn begins, and prints out the total number of turns once the game has ended.

DiceGameTracker实现了DiceGameDelegate协议要求的三个方法,用来记录游戏已经进行的轮数。当游戏开始时,numberOfTurns属性被赋值为0,然后在每新一轮中递增,游戏结束后,打印游戏的总轮数。

The implementation ofgameDidStart(_:)shown above uses thegameparameter to print some introductory information about the game that is about to be played. Thegameparameter has a type ofDiceGame, notSnakesAndLadders, and sogameDidStart(_:)can access and use only methods and properties that are implemented as part of theDiceGameprotocol. However, the method is still able to use type casting to query the type of the underlying instance. In this example, it checks whethergameis actually an instance ofSnakesAndLaddersbehind the scenes, and prints an appropriate message if so.

gameDidStart(_:)方法从game参数获取游戏信息并打印。game参数是DiceGame类型而不是SnakeAndLadders类型,所以在gameDidStart(_:)方法中只能访问DiceGame协议中的内容。当然了,SnakeAndLadders的方法也可以在类型转换之后调用。在上例代码中,通过is操作符检查game是否为SnakesAndLadders类型的实例,如果是,则打印出相应的消息。

ThegameDidStart(_:)method also accesses thediceproperty of the passedgameparameter. Becausegameis known to conform to theDiceGameprotocol, it is guaranteed to have adiceproperty, and so thegameDidStart(_:)method is able to access and print the dice’ssidesproperty, regardless of what kind of game is being played.

无论当前进行的是何种游戏,由于game符合DiceGame协议,可以确保game含有dice属性。因此在gameDidStart(_:)方法中可以通过传入的game参数来访问dice属性,进而打印出dice的sides属性的值。

Here’s howDiceGameTrackerlooks in action:

DiceGameTracker的运行情况如下所示:

let tracker=DiceGameTracker()

let game=SnakesAndLadders()

game.delegate=tracker

game.play()

// Started a new game of Snakes and Ladders

// The game is using a 6-sided dice

// Rolled a 3

// Rolled a 5

// Rolled a 4

// Rolled a 5

// The game lasted for 4 turns

Adding Protocol Conformance with an Extension (通过扩展添加协议一致性)

You can extend an existing type to adopt and conform to a new protocol, even if you do not have access to the source code for the existing type. Extensions can add new properties, methods, and subscripts to an existing type, and are therefore able to add any requirements that a protocol may demand. For more about extensions, seeExtensions.

即便无法修改源代码,依然可以通过扩展令已有类型遵循并符合协议。扩展可以为已有类型添加属性、方法、下标以及构造器,因此可以符合协议中的相应要求。详情请在扩展章节中查看。

NOTE

Existing instances of a type automatically adopt and conform to a protocol when that conformance is added to the instance’s type in an extension.

通过扩展令已有类型遵循并符合协议时,该类型的所有实例也会随之获得协议中定义的各项功能。

For example, this protocol, calledTextRepresentable, can be implemented by any type that has a way to be represented as text. This might be a description of itself, or a text version of its current state:

例如下面这个TextRepresentable协议,任何想要通过文本表示一些内容的类型都可以实现该协议。这些想要表示的内容可以是实例本身的描述,也可以是实例当前状态的文本描述:

protocol TextRepresentable{

    var textualDescription:String{get}

}

TheDiceclass from earlier can be extended to adopt and conform toTextRepresentable:

可以通过扩展,令先前提到的Dice类遵循并符合TextRepresentable协议:

extension Dice:TextRepresentable{

    var textualDescription:String{

    return"A\(sides)-sided dice"

  }

}

This extension adopts the new protocol in exactly the same way as ifDicehad provided it in its original implementation. The protocol name is provided after the type name, separated by a colon, and an implementation of all requirements of the protocol is provided within the extension’s curly braces.

通过扩展遵循并符合协议,和在原始定义中遵循并符合协议的效果完全相同。协议名称写在类型名之后,以冒号隔开,然后在扩展的大括号内实现协议要求的内容。

AnyDiceinstance can now be treated asTextRepresentable:

现在所有Dice的实例都可以看做TextRepresentable类型:

let d12=Dice(sides:12,generator:LinearCongruentialGenerator())

print(d12.textualDescription)

// Prints "A 12-sided dice"

Similarly, theSnakesAndLaddersgame class can be extended to adopt and conform to theTextRepresentableprotocol:

同样,SnakesAndLadders类也可以通过扩展遵循并符合TextRepresentable协议:

extension SnakesAndLadders:TextRepresentable{

   var textualDescription:String{

    return "A game of Snakes and Ladders with\(finalSquare)squares"

  }

}

print(game.textualDescription)

// Prints "A game of Snakes and Ladders with 25 squares"

上一篇下一篇

猜你喜欢

热点阅读