Swift4 基礎部分: Protocols(協議)

本文是學習《The Swift Programming Language》整理的相關隨筆,基本的語法不作介紹,主要介紹Swift中的一些特性或者與OC差異點。

系列文章:

Protocol Syntax(協議語法)

struct SomeStructure: FirstProtocol, AnotherProtocol {
    // structure definition goes here
}

// 如果是存在繼承關系,父類放在第
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
    // class definition goes here
}

Property Requirements(屬性要求)

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

例子:

protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
}

完整例子:

protocol FullyNamed {
    var fullName:String {get}
}

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");
print("\(ncc1701.fullName)");

執行結果:

USS Enterprise

Method Requirements(方法要求)

協議支持類方法與實例方法。

例子:

protocol RandomNumberGenerator {
    static func factor() -> Double;
    func random() -> Double;
}

class LinearCongruentialGenerator: RandomNumberGenerator {
    var lastRandom = 42.0;
    let m = 139968.0;
    let a = 3877.0;
    let c = 29573.0;
    
    static func factor() -> Double {
        return 0.5;
    }
    
    func random() -> Double {
        lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy:m));
        return lastRandom / m * LinearCongruentialGenerator.factor();
    }
}

let generator = LinearCongruentialGenerator();
print("Here's a random number: \(generator.random())");

執行結果:

Here's a random number: 0.187324959990855

Mutating Method Requirements(Mutating 方法要求)

針對枚舉或者結構體中的方法需要修改其中的實例時。

例子:

protocol Togglable {
    mutating func toggle();
}

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();

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 the required modifier.
  • 可以在實現協議的類中實現構造器,無論是作為指定構造器,還是作為便利構造器。
  • 必須為構造器實現標上required修飾符。

例子:

protocol SomeProtocol {
    init(someParameter:Int);
}

class SomeClass :SomeProtocol {
    init(someParameter: Int) {
        
    }
}

報錯信息:


error: SwfitStudy.playground:436:5: error: initializer requirement 'init(someParameter:)' can only be satisfied by a `required` initializer in non-final class 'SomeClass'
    init(someParameter: Int) {
    ^
    required 

正確處理:

protocol SomeProtocol {
    init(someParameter:Int);
}

class SomeClass :SomeProtocol {
    required init(someParameter: Int) {
        
    }
}
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 the required 
and override modifiers
  • 如果實現了一個子類重寫了父類的構造器,同時實現了一個協議中同樣的構造器方法,需要required,override修飾該構造器。

例子:

protocol SomeProtocol {
    init();
}

class SomeSuperClass {
    init() {
        
    }
}

class SomeSubClass:SomeSuperClass,SomeProtocol {
    required override init() {
        
    }
}

Protocols as Types(協議作為類型)

與OC中類似,不展開說明。

例子:

protocol RandomNumberGenerator {
    static func factor() -> Double;
    func random() -> Double;
}

class LinearCongruentialGenerator: RandomNumberGenerator {
    var lastRandom = 42.0;
    let m = 139968.0;
    let a = 3877.0;
    let c = 29573.0;
    
    static func factor() -> Double {
        return 0.5;
    }
    
    func random() -> Double {
        lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy:m));
        return lastRandom / m * LinearCongruentialGenerator.factor();
    }
}

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

var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...5 {
    print("Random dice roll is \(d6.roll())");
}

執行結果:

Random dice roll is 2
Random dice roll is 3
Random dice roll is 2
Random dice roll is 3
Random dice roll is 2

Delegation (代理)

代理與OC中的類似,不展開說明。

例子:

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

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

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 2
Rolled a 3
Rolled a 2
Rolled a 3
Rolled a 2
Rolled a 1
Rolled a 2
Rolled a 1
Rolled a 1
Rolled a 2
Rolled a 1
Rolled a 3
Rolled a 2
Rolled a 1
Rolled a 2
Rolled a 1
Rolled a 3
Rolled a 2
Rolled a 2
Rolled a 3
Rolled a 2
The game lasted for 21 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. 
  • 即便無法修改源代碼,依然可以通過擴展來遵循新的協議。

例子:

protocol TextRepresentable {
    var textualDescription: String { get }
}

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

let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator());
print(d12.textualDescription);

執行結果:

A 12-sided dice

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

執行結果:

A hamster named Simon

Collections of Protocol Types(協議類型的集合) 與 Protocol Inheritance(協議繼承)

與OC中類似,不擴展。

Class-Only Protocols(類類型協議)

You can limit protocol adoption to class types (and not structures 
or enumerations) by adding the AnyObject protocol to a protocol’s 
inheritance list.
  • 那你可以限制協議只能被類類型遵循,讓該協議遵循AnyObject協議即可。
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
    // class-only protocol definition goes here
}

Protocol Composition (協議合成)

It can be useful to require a type to conform to multiple protocols 
at once. You can combine multiple protocols into a single 
requirement with a protocol composition. Protocol compositions 
behave like you defined a temporary local protocol that has the 
combined requirements of all protocols in the composition. Protocol 
compositions don’t define any new protocol types.

Protocol compositions have the form SomeProtocol & AnotherProtocol. 
You can list as many protocols as you need to, separating them by 
ampersands (&). In addition to its list of protocols, a protocol 
composition can also contain one class type, which lets you specify a required superclass.
  • 如果一種類型需要遵循多個協議,可以將多個協議組合。協議組合的方式類似如下:SomeProtocol & AnotherProtocol

例子:

protocol Named {
    var name:String {get}
}

protocol Aged {
    var age:Int {get}
}

struct Person:Named,Aged {
    var name:String;
    var age:Int;
}

func wishHappyBirthday(to celebrator:Named & Aged) {
    print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}

let birthdayPerson = Person(name:"Malcolm", age:21);
wishHappyBirthday(to: birthdayPerson);

執行結果:

Happy birthday, Malcolm, you're 21!

Checking for Protocol Conformance (檢查協議一致性)

You can use the is and as operators described in Type Casting to 
check for protocol conformance, and to cast to a specific protocol. 
  • 可以利用is或者as來檢測類型是否遵循某個協議。

  • is用來檢查實例是否符合某個協議,若符合則返回true,否則返回false

  • as? 返回一個可選值,當實例符合某個協議時,返回類型為協議類型的可選值,否則返回nil

  • as! 將實例強制向下轉換到某個協議類型,如果強轉失敗,會引發運行時錯誤。

OC中則是利用如下的方式,以下兩者都可以:

+ (BOOL)conformsToProtocol:(Protocol *)protocol;
- (BOOL)conformsToProtocol:(Protocol *)protocol;

例子:

protocol HasArea {
    var area: Double {get}
}

class Circle:HasArea {
    let pi = 3.1415927;
    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 {
    var legs:Int;
    init(legs:Int) {
        self.legs = legs;
    }
}

let objects:[AnyObject] = [
    Circle(radius:2.0),
    Country(area:243_610),
    Animal(legs:4)
];

for object in objects {
    if object is HasArea {
        let objectWithArea = object as? HasArea;
        print("Area is \(objectWithArea?.area)");
    }else{
        print("Something that doesn't have an area");
    }
}

執行結果:

Area is 12.5663708
Area is 243610.0
Something that doesn't have an area

Optional Protocol Re quirements(可選的協議要求)

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. 
  • 協議中如果是需要使用可選的協議要求,協議使用@objc修飾,協議要求使用@objc optional修飾。

例子:

import Foundation

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

class Counter {
    var count = 0;
    var dataSource: CounterDataSource?;
    func increment() {
        if let amount = dataSource?.increment?(forCount: count) {
            count += amount;
        } else if let amount = dataSource?.fixedIncrement {
            count += amount;
        }
    }
}


var counter = Counter();
class TowardsZeroSource:CounterDataSource {
    func increment(forCount count: Int) -> Int {
        if count == 0 {
            return 0;
        } else if count < 0 {
            return 1;
        } else {
            return -1;
        }
    }
}

counter.count = -4;
counter.dataSource = TowardsZeroSource();
for _ in 1...5 {
    counter.increment();
    print(counter.count);
}

執行結果:

-3
-2
-1
0
0

Protocol Extensions (協議擴展)

Protocols can be extended to provide method and property 
implementations to conforming types.
  • 協議可以通過擴展來為遵循協議的類型提供屬性、方法以及下標的實現。

例子:

protocol RandomNumberGenerator {
    static func factor() -> Double;
    func random() -> Double;
}

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

class LinearCongruentialGenerator: RandomNumberGenerator {
    var lastRandom = 42.0;
    let m = 139968.0;
    let a = 3877.0;
    let c = 29573.0;

    static func factor() -> Double {
        return 0.5;
    }

    func random() -> Double {
        lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy:m));
        return lastRandom / m * LinearCongruentialGenerator.factor();
    }
}


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

執行結果:

Here's a random number: 0.187324959990855
And here's a random Boolean: false

Providing Default Implementations(提供默認的實現)

You can use protocol extensions to provide a default implementation 
to any method or computed property requirement of that protocol.
  • 可以通過協議擴展來為協議要求的屬性、方法以及下標提供默認的實現。

例子:

protocol RandomNumberGenerator {
    static func factor() -> Double;
    func random() -> Double;
}

extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
    
    // 默認的實現
    static func factor() -> Double {
        return 0.5;
    }
}

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 * LinearCongruentialGenerator.factor();
    }
}


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

執行結果:

Here's a random number: 0.187324959990855
And here's a random Boolean: false

Adding Constraints to Protocol Extensions (為協議擴展添加限制條件)

例子:


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

protocol TextRepresentable {
    var textualDescription: String { get }
}

extension Hamster: TextRepresentable {}

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];
print(hamsters.textualDescription)

執行結果:

[A hamster named Murray, A hamster named Morgan, A hamster named Maurice]
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容