Swift4 基礎(chǔ)部分: Protocols(協(xié)議)

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

系列文章:

Protocol Syntax(協(xié)議語法)

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

// 如果是存在繼承關(guān)系,父類放在第
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)");

執(zhí)行結(jié)果:

USS Enterprise

Method Requirements(方法要求)

協(xié)議支持類方法與實例方法。

例子:

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

執(zhí)行結(jié)果:

Here's a random number: 0.187324959990855

Mutating Method Requirements(Mutating 方法要求)

針對枚舉或者結(jié)構(gòu)體中的方法需要修改其中的實例時。

例子:

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 (構(gòu)造器要求)

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.
  • 可以在實現(xiàn)協(xié)議的類中實現(xiàn)構(gòu)造器,無論是作為指定構(gòu)造器,還是作為便利構(gòu)造器。
  • 必須為構(gòu)造器實現(xiàn)標(biāo)上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
  • 如果實現(xiàn)了一個子類重寫了父類的構(gòu)造器,同時實現(xiàn)了一個協(xié)議中同樣的構(gòu)造器方法,需要required,override修飾該構(gòu)造器。

例子:

protocol SomeProtocol {
    init();
}

class SomeSuperClass {
    init() {
        
    }
}

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

Protocols as Types(協(xié)議作為類型)

與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())");
}

執(zhí)行結(jié)果:

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

執(zhí)行結(jié)果:

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 (通過擴展添加協(xié)議一致性)

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. 
  • 即便無法修改源代碼,依然可以通過擴展來遵循新的協(xié)議。

例子:

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

執(zhí)行結(jié)果:

A 12-sided dice

Declaring Protocol Adoption with an Extension(通過擴展遵循協(xié)議)

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.
  • 如果一個類型已經(jīng)實現(xiàn)了協(xié)議中要求但是卻沒有遵循協(xié)議,可以利用一個空的擴展遵循該協(xié)議。

例子:

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

執(zhí)行結(jié)果:

A hamster named Simon

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

與OC中類似,不擴展。

Class-Only Protocols(類類型協(xié)議)

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

Protocol Composition (協(xié)議合成)

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.
  • 如果一種類型需要遵循多個協(xié)議,可以將多個協(xié)議組合。協(xié)議組合的方式類似如下: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);

執(zhí)行結(jié)果:

Happy birthday, Malcolm, you're 21!

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

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來檢測類型是否遵循某個協(xié)議。

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

  • as? 返回一個可選值,當(dāng)實例符合某個協(xié)議時,返回類型為協(xié)議類型的可選值,否則返回nil

  • as! 將實例強制向下轉(zhuǎn)換到某個協(xié)議類型,如果強轉(zhuǎn)失敗,會引發(fā)運行時錯誤。

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

執(zhí)行結(jié)果:

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

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

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. 
  • 協(xié)議中如果是需要使用可選的協(xié)議要求,協(xié)議使用@objc修飾,協(xié)議要求使用@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);
}

執(zhí)行結(jié)果:

-3
-2
-1
0
0

Protocol Extensions (協(xié)議擴展)

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

例子:

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

執(zhí)行結(jié)果:

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

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

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

例子:

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

extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
    
    // 默認的實現(xiàn)
    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())");

執(zhí)行結(jié)果:

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

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

例子:


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)

執(zhí)行結(jié)果:

[A hamster named Murray, A hamster named Morgan, A hamster named Maurice]
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,720評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,690評論 2 370

推薦閱讀更多精彩內(nèi)容