swift入门21 协议

协议语法

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
}

属性要求

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 gettable and settable.

一个协议可以任何要遵循此协议的类型提供一个具有特定名字或类型的实例属性或类型属性。此协议不指定该属性是存储属性或计算属性,协议只指定属性的名称和类型。此协议还指定每个属性必须是gettable的或必须同时是gettable和settable的。

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.

如果一个协议要求一个属性同时是gettable的和settable的,那么此属性就不能是一个常量存储属性或一个只读计算属性。如果此协议只要求一个属性是gettable的,那么此属性可以是属性,同时,如果你的代码需要,属性也可以是settable的。(这段翻译有点怪怪的。。。。。。好像不太对。。。。)

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

属性要求永远声明为变量属性,以key关键字开头。gettable和settable属性用{ get set } 表明,{ get set } 写在它们的类型声明之后,gettable属性用 { get }标明。

protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
}
protocol AnotherProtocol {
    static var someTypeProperty: Int { get set }
}

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

协议中的类别属性要加上static关键字。即使是一个用class或static标记的,由类实现的类别属性,上诉规则也一样适用。

protocol AnotherProtocol {
    static var someTypeProperty: Int { get set }
}

来个例子,一个只有一个实例属性的协议:

protocol FullyNamed {
    var fullName: String { get }
}

The FullyNamed protocol 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 any FullyNamed type must have a gettable instance property called fullName, which is of type String.

FullyNamed协议要求遵循此协议的类型提供一个完全限定的名字( fully-qualified name).此协议只指定了遵循协议的类型必须提供一个全名,其他的什么也不限制。此协议声明:任何FullyNamed类行必须有一个gettable的类型为字符串的实例属性fullName。

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

struct Person: FullyNamed {
    var fullName: String
}
let john = Person(fullName: "John Appleseed")
// john.fullName is "John Appleseed"

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

这个例子定义了一个名为Person的结构,代表了一个具体名字的人(person).它声明它遵循FullyNamed协议。

Each instance of Person has a single stored property called fullName, which is of type String. This matches the single requirement of the FullyNamed protocol, and means that Person has correctly conformed to the protocol. (Swift reports an error at compile-time if a protocol requirement is not fulfilled.)

每个person实例都有一个字符串类型的存储属性:fullName。这符合FullyNamed协议的属性要求,也表明person正确地遵循了FullyNamed协议。(如果一个协议的要求没有完成,swift将报一个编译时错误)。

下面是一个更复杂点的例子:

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"

方法要求

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 the static keyword when they are defined in a protocol. This is true even though type method requirements are prefixed with the class or static keyword when implemented by a class:

和类型属性要求一样,协议定义中的类型方法前也必须加上关键字static。

protocol SomeProtocol {
    static func someTypeMethod()
}

下面例子中的协议只要求一个实例方法:

protocol RandomNumberGenerator {
    func random() -> Double
}

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

RandomNumberGenerator协议要求任何遵循此协议的类型都要有一个实例方法random,该方法返回一个Double值。即使这不是协议要求的,但是还是假设它的值将是一个从0.0到1.0(不包括1.0)的值。

The RandomNumberGenerator protocol 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协议不要求每个随机数字(random number)怎样产生,它只要求生成器提供一个标准方法去产生一个新的随机数。

下面的例子是一个遵循了RandomNumberGenerator协议的类的实现

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"

修改方法要求

It is sometimes necessary for a method to modify (or mutate) the instance it belongs to. For instance methods on value types (that is, structures and enumerations) you place the mutating keyword before a method’s func keyword to indicate that the method is allowed to modify the instance it belongs to and any properties of that instance.

有时候我们需要让一个方法修改它所属的实例。值类型(即结构和枚举)的实例方法,在方法等func关键字前加上mutating关键字以此表明这个方法允许修改其所属的实例,包括修改此实例的任意属性。

The example below defines a protocol called Togglable, which defines a single instance method requirement called toggle.

下面例子定义了一个协议Togglable,协议中定义了一个实例方法toggle:

protocol Togglable {
    mutating func toggle()
}

If you implement the Togglable protocol for a structure or enumeration, that structure or enumeration can conform to the protocol by providing an implementation of the toggle() method that is also marked as mutating.

如果有一个结构或枚举实现了Togglable协议,那么这个结构或枚举就需要提供一个同样标记为mutating的toggle() 方法的实现。

The example below defines an enumeration called OnOffSwitch. This enumeration toggles between two states, indicated by the enumeration cases on and off. The enumeration’s toggle implementation is marked as mutating, to match the Togglable protocol’s requirements:

下面的例子定义了一个枚举OnOffSwitch。这个枚举的功能是切换两个状态,分别是of 和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

协议作为类型

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:

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

As the type of a constant, variable, or property

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

由于协议是一种类型,所以你可以像其他类型一样使用协议,比如:

作为一个函数,方法或初始化方法的参数类型或返回类型

作为一个常量,变量或属性的类型

作为一个数组,字典或其他容器中的项的类型。

举个例子:

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
    }
}

The generator property is of type RandomNumberGenerator. Therefore, you can set it to an instance of any type that adopts the RandomNumberGenerator protocol. Nothing else is required of the instance you assign to this property, except that the instance must adopt the RandomNumberGenerator protocol.

generator属性是一个RandomNumberGenerator类型。所以,你可以把它设为一个遵循RandomNumberGenerator协议的任意类型的实例。只需要这个实例遵循了RandomNumberGenerator协议即可,其他的没有要求。

Dice also has an initializer, to set up its initial state. This initializer has a parameter called generator, which is also of type RandomNumberGenerator. You can pass a value of any conforming type in to this parameter when initializing a new Dice instance.

Dice还有一个初始化方法,用来设置它的初始状态。这个初始化方法有一个参数generator,generator的类型也是RandomNumberGenerator。你可以在初始化新的Dice实例时把任意遵循了RandomNumberGenerator协议的类型的值传给generator参数。

看个例子:

var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...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 is a design pattern that enables a class or structure to hand off (or delegate) 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.

代理是一种可以使一个类或结构去处理(或说代理)另一个类型的实例的功能的设计模式。这种设计模式的实现:定义一个封装了需要被代理的功能的协议,由此一个遵循此协议的类型(成为代理)就必须提供那些需要被代理的功能。代理可以用了相应一个特定的行为,或从外部资源获取数据,而不需要知道外部资源的类型。

看例子:

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)
}

下面例子中的SnakesAndLadders遵循了DiceGame协议,并有一个代理:

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: while square != finalSquare {
            let diceRoll = dice.roll()
            delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
            switch square + diceRoll {
            case finalSquare:
                break gameLoop
            case let newSquare where newSquare > finalSquare:
                continue gameLoop
            default:
                square += diceRoll
                square += board[square]
            }
        }
        delegate?.gameDidEnd(self)
    }
}

下一个例子DiceGameTracker遵循了DiceGameDelegate协议:

class DiceGameTracker: DiceGameDelegate {
    var numberOfTurns = 0
    func gameDidStart(_ game: DiceGame) {
        numberOfTurns = 0
        if game is SnakesAndLadders {
            print("Started a new game of Snakes and Ladders")
        }
        print("The game is using a \(game.dice.sides)-sided dice")
    }
    func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
        numberOfTurns += 1
        print("Rolled a \(diceRoll)")
    }
    func gameDidEnd(_ game: DiceGame) {
        print("The game lasted for \(numberOfTurns) turns")
    }
}

它们的使用:

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

在拓展中增加协议

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.

你可以拓展一个类,使其遵循某个协议,即使你无法访问该类的源码。拓展可以增加新属性,方法,订阅,因此也能增加一个协议要求的各种条件。

举个例子,有个协议TextRepresentable:

protocol TextRepresentable {
    var textualDescription: String { get }
}

The Dice class from earlier can be extended to adopt and conform to TextRepresentable:

我们可以拓展之前的dice类,使其遵循TextRepresentable协议:

extension Dice: TextRepresentable {
    var textualDescription: String {
        return "A \(sides)-sided dice"
    }
}

let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
print(d12.textualDescription)
// Prints "A 12-sided dice"

Similarly, the SnakesAndLadders game class can be extended to adopt and conform to the TextRepresentable protocol:

同样,可以拓展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"

用拓展声明协议的遵循(Declaring Protocol Adoption with an Extension)

If a type already conforms to all of the requirements of a protocol, but has not yet stated that it adopts that protocol, you can make it adopt the protocol with an empty extension:

如果一个类型已经遵循了一个协议,但是还没有声明它遵循此协议,你可以用一个空的拓展来声明:

struct Hamster {
    var name: String
    var textualDescription: String {
        return "A hamster named \(name)"
    }
}
extension Hamster: TextRepresentable {}

let simonTheHamster = Hamster(name: "Simon")
let somethingTextRepresentable: TextRepresentable = simonTheHamster
print(somethingTextRepresentable.textualDescription)
// Prints "A hamster named Simon"

协议类型的集合

A protocol can be used as the type to be stored in a collection such as an array or a dictionary, as mentioned in Protocols as Types. This example creates an array of TextRepresentable things:

协议可以作为集合存储的类型,就像数组,字典一样。下面的例子创建了一个TextRepresentable数组things:

let things: [TextRepresentable] = [game, d12, simonTheHamster]

It is now possible to iterate over the items in the array, and print each item’s textual description:

现在可以遍历该数组中的项,然后打印出每项的描述:

for thing in things {
    print(thing.textualDescription)
}
// A game of Snakes and Ladders with 25 squares
// A 12-sided dice
// A hamster named Simon

Note that the thing constant is of type TextRepresentable.

注意,常量thing的类型是TextRepresentable。

遵循类型只能为类的协议(Class-Only Protocols)

You can limit protocol adoption to class types (and not structures or enumerations) by adding the class keyword to a protocol’s inheritance list. The class keyword must always appear first in a protocol’s inheritance list, before any inherited protocols:

你可以限制协议的遵循类型为类(不能是结构或枚举),只需在协议的继承列表中添加关键字class。class关键字必须在协议的继承列表的首位:

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // class-only protocol definition goes here
}

In the example above, SomeClassOnlyProtocol can only be adopted by class types. It is a compile-time error to write a structure or enumeration definition that tries to adopt SomeClassOnlyProtocol.

上面例子中,遵循SomeClassOnlyProtocol协议的类型只能是类。如果结构或枚举来遵循SomeClassOnlyProtocol协议将会报一个编译时错误。

可选择实现的协议要求

You can define optional requirements for protocols, These requirements do not have to be implemented by types that conform to the protocol. Optional requirements are prefixed by the optional modifier as part of the protocol’s definition. Optional requirements are available so that you can write code that interoperates with Objective-C. Both the protocol and the optional requirement must be marked with the @objc attribute. Note that @objc protocols can be adopted only by classes that inherit from Objective-C classes or other @objc classes. They can’t be adopted by structures or enumerations.

你可以为协议定义可选择实现的要求(optional requirements).这些要求并不需要遵循此协议的类型一定实现。可选的要求由optional标记。协议和可选择实现的要求都必须用@objc标记。注意,标记为@objc的协议只能由继承自oc的类或其他@objc类的类遵循,不能被结构或枚举类型遵循。

@objc protocol CounterDataSource {
    @objc optional func increment(forCount count: Int) -> Int
    @objc optional var fixedIncrement: Int { get }
}

协议的拓展

Protocols can be extended to provide method and property implementations to conforming types. This allows you to define behavior on protocols themselves, rather than in each type’s individual conformance or in a global function.

可以拓展协议,以提供方法和属性。

For example, the RandomNumberGenerator protocol can be extended to provide a randomBool() method, which uses the result of the required random() method to return a random Bool value:

举例来说,RandomNumberGenerator可以被拓展,提供了一个randomBool() 方法,该方法使用必须的random()方法返回的结果来返回一个随机的Bool值:

extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
}

By creating an extension on the protocol, all conforming types automatically gain this method implementation without any additional modification.

创建了协议的拓展后,所有遵循此协议的类型都自动的获取了此实现,而不用做额外的改动:

let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Prints "Here's a random number: 0.37464991998171"
print("And here's a random Boolean: \(generator.randomBool())")
// Prints "And here's a random Boolean: true"

提供默认实现

You can use protocol extensions to provide a default implementation to any method or computed property requirement of that protocol. If a conforming type provides its own implementation of a required method or property, that implementation will be used instead of the one provided by the extension.

你可以使用协议的拓展来为方法或计算属性提供一个默认的实现。如果一个遵循此协议的类型提供了自己的实现,那么此实现会替代拓展中的默认实现。

extension PrettyTextRepresentable  {
    var prettyTextualDescription: String {
        return textualDescription
    }
}

给协议拓展添加限制

When you define a protocol extension, you can specify constraints that conforming types must satisfy before the methods and properties of the extension are available. You write these constraints after the name of the protocol you’re extending using a generic where clause, as described in Generic Where Clauses.

当你定义一个协议拓展的时候,你可以为此拓展添加的方法和属性指定一些必须满足的限制条件。这些限制条件写在协议名之后,用一个where范型从句:

For instance, you can define an extension to the Collection protocol that applies to any collection whose elements conform to the TextRepresentable protocol from the example above.

举个例子,你可以为Collection协议定义一个拓展:该拓展适用于元素遵循TextRepresentable协议的任何集合(collection)类型。

extension Collection where Iterator.Element: TextRepresentable {
    var textualDescription: String {
        let itemsAsText = self.map { $0.textualDescription }
        return "[" + itemsAsText.joined(separator: ", ") + "]"
    }
}

let murrayTheHamster = Hamster(name: "Murray")
let morganTheHamster = Hamster(name: "Morgan")
let mauriceTheHamster = Hamster(name: "Maurice")
let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]

Because Array conforms to Collection and the array’s elements conform to the TextRepresentable protocol, the array can use the textualDescription property to get a textual representation of its contents:

由于数组是遵循Collection协议的,而数组中的元素又遵循了TextRepresentable协议,因此该数组可以使用textualDescription属性来表示它的内容:

print(hamsters.textualDescription)
// Prints "[A hamster named Murray, A hamster named Morgan, A hamster named Maurice]"

你可能感兴趣的:(swift入门21 协议)