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]
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容