Swift4 基础部分: Protocols(协议)
2017-12-09 本文已影响94人
Arnold134777
本文是学习《The Swift Programming Language》整理的相关随笔,基本的语法不作介绍,主要介绍Swift中的一些特性或者与OC差异点。
系列文章:
- Swift4 基础部分:The Basics
- Swift4 基础部分:Basic Operators
- Swift4 基础部分:Strings and Characters
- Swift4 基础部分:Collection Types
- Swift4 基础部分:Control Flow
- Swift4 基础部分:Functions
- Swift4 基础部分:Closures
- Swift4 基础部分: Enumerations
- Swift4 基础部分: Classes and Structures
- Swift4 基础部分: Properties
- Swift4 基础部分: Methods
- Swift4 基础部分: Subscripts
- Swift4 基础部分: Inheritance
- Swift4 基础部分: Initialization
- Swift4 基础部分: Deinitialization
- Swift4 基础部分: Automatic Reference Counting(自动引用计数)
- Swift4 基础部分: Optional Chaining(可选链)
- Swift4 基础部分: Error Handling(错误处理)
- Swift4 基础部分: Type Casting(类型转换)
- Swift4 基础部分: Nested Types(嵌套类型)
- Swift4 基础部分: Extensions(扩展)
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]