本文是學(xué)習(xí)《The Swift Programming Language》整理的相關(guān)隨筆,基本的語法不作介紹,主要介紹Swift中的一些特性或者與OC差異點。
系列文章:
- Swift4 基礎(chǔ)部分:The Basics
- Swift4 基礎(chǔ)部分:Basic Operators
- Swift4 基礎(chǔ)部分:Strings and Characters
- Swift4 基礎(chǔ)部分:Collection Types
- Swift4 基礎(chǔ)部分:Control Flow
- Swift4 基礎(chǔ)部分:Functions
- Swift4 基礎(chǔ)部分:Closures
- Swift4 基礎(chǔ)部分: Enumerations
- Swift4 基礎(chǔ)部分: Classes and Structures
- Swift4 基礎(chǔ)部分: Properties
- Swift4 基礎(chǔ)部分: Methods
- Swift4 基礎(chǔ)部分: Subscripts
- Swift4 基礎(chǔ)部分: Inheritance
- Swift4 基礎(chǔ)部分: Initialization
- Swift4 基礎(chǔ)部分: Deinitialization
- Swift4 基礎(chǔ)部分: Automatic Reference Counting(自動引用計數(shù))
- Swift4 基礎(chǔ)部分: Optional Chaining(可選鏈)
- Swift4 基礎(chǔ)部分: Error Handling(錯誤處理)
- Swift4 基礎(chǔ)部分: Type Casting(類型轉(zhuǎn)換)
- Swift4 基礎(chǔ)部分: Nested Types(嵌套類型)
- Swift4 基礎(chǔ)部分: Extensions(擴展)
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]