Swift常用语法示例代码(三)

此篇文章整理自我以前学习Swift时的一些练习代码,其存在的意义多是可以通过看示例代码更快地回忆Swift的主要语法。

如果你想系统学习Swift或者是Swift的初学者请绕路,感谢GithubThe Swift Programming Language开源翻译的中文版,感谢极客学院wiki提供的PDF版本。

代码和PDF版本上传至Github,有兴趣的可以下载下来试试。

SwiftOptionalChain

Swift可选链其实是可选类型的扩展使用。只需记得有值就将其包裹成可选类型,无值被是nil。理解所给出的例子即可,教程的文字说的复杂了。

import Foundation

// 如果可空的目标有值,那么调用就会成功;如果选择的目标为空(nil),那么这种调用将返回空(nil)
// 多个连续的调用可以被链接在一起形成一个调用链,如果其中任何一个节点为 空(nil)将导致整个链调用失败。

// ----------------- Part 1 -------------------------
// 下面几段代码将解释可空链式调用和强制展开的不同。
class Person {
    var residence: Residence?
}
class Residence {
    var numberOfRooms = 1
}
let john = Person()

// 如果使用叹号强制展开获得这个john的residence属性中的numberOfRooms值,会触发运行时错误
// let roomCount = john.residence!.numberOfRooms

// 可空链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可空类型值
if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}

john.residence = Residence()
if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}


// ----------------- Part 2 -------------------------
// 通过使用可空链式调用可以调用多层属性,方法,和下标。这样可以通过各种模型向下访问各种子属性。
// 并且判断能否访问子属性的属性,方法或下标
class Person {
    var residence: Residence?
}
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        get {
            return rooms[i]
            }
        set {
            rooms[i] = newValue
            }
    }
    func printNumberOfRooms() {
        print("The number of rooms is \(numberOfRooms)")
    }
    var address: Address?
}
class Room {
    let name: String
    init(name: String) { self.name = name }
}
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if buildingName != nil {
            return buildingName
        } else if buildingNumber != nil {
            return buildingNumber
            } else {
                return nil
            }
    }
}

let john = Person()
if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}

let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress

if john.residence?.printNumberOfRooms() != nil {
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}

if (john.residence?.address = someAddress) != nil {
    print("It was possible to set the address.")
} else {
    print("It was not possible to set the address.")
}

if let firstRoomName = john.residence?[0].name {
    print("The first room name is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}

john.residence?[0] = Room(name: "Bathroom")

let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "Living Room"))
johnsHouse.rooms.append(Room(name: "Kitchen"))
john.residence = johnsHouse

if let firstRoomName = john.residence?[0].name {
    print("The first room name is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}


// ----------------- Part 3 -------------------------
// 如果下标返回可空类型值,比如Swift中 Dictionary 的 key 下标。可以在下标的闭合括号后面放一个问号来链接下标的可空返回值
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++
testScores["Brian"]?[0] = 72


// ----------------- Part 4 -------------------------
if let johnsStreet = john.residence?.address?.street {
} else {
    print("Unable to retrieve the address.")
}

let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence?.address = johnsAddress

if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \(johnsStreet).")
} else {
    print("Unable to retrieve the address.")
}

if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
    print("John's building identifier is \(buildingIdentifier).")
}

if let beginsWithThe = john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
    if beginsWithThe {
        print("John's building identifier begins with \"The\".")
    } else {
        print("John's building identifier does not begin with \"The\".")
        }
}

SwiftErrorHanding

错误处理(Error handling)是响应错误以及从错误中恢复的过程。swift提供了在运行对可恢复错误抛出,捕 获,传送和操作的高级支持。iOS的很多内置类型用到错误抛出,所以错误处理使用频率很高。


import Foundation

// Swift中有 4 种处理错误的方式。
// 你可以把函数抛出的错误传递给调用此函数的代码、用 do-catch 语句处理错误、将错误作为可选类型处理、或者断言此错误根本不会发生。

// ----------------- Part 1 -----------------
// 用 throws 关键字标识一个可抛出错误的函数,方法或是构造器。在函数声明中的参数列表之后加上 throws
enum VendingMachineError: ErrorType {
    case InvalidSelection
    case OutOfStock
    case InsufficientFunds(coinsNeeded: Int)
}
struct Item {
    var price: Int
    var count: Int
}

class VendingMachine {
    var inventory = [
        "Candy Bar": Item(price: 12, count: 7),
        "Chips": Item(price: 10, count: 4),
        "Pretzels": Item(price: 7, count: 11)
    ]
    var coinsDeposited = 0
    func dispenseSnack(snack: String) {
        print("Dispensing \(snack)")
    }
    func vend(itemNamed name: String) throws {
        guard var item = inventory[name] else {
            throw VendingMachineError.InvalidSelection
        }
        guard item.count > 0 else {
            throw VendingMachineError.OutOfStock
        }
        guard item.price <= coinsDeposited else {
            throw VendingMachineError.InsufficientFunds(coinsNeeded: item.price - coinsDeposited)
        }
        coinsDeposited -= item.price
        --item.count
        inventory[name] = item
        dispenseSnack(name)
    }
}

let favoriteSnacks = [
    "Alice": "Chips",
    "Bob": "Licorice",
    "Eve": "Pretzels",
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
    let snackName = favoriteSnacks[person] ?? "Candy Bar"
    try vendingMachine.vend(itemNamed: snackName)
}

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
    try buyFavoriteSnack("Alice", vendingMachine: vendingMachine)
} catch VendingMachineError.InvalidSelection {
    print("Invalid Selection")
} catch VendingMachineError.OutOfStock {
    print("Out of Stock")
} catch VendingMachineError.InsufficientFunds(let coinsNeeded) {
    print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
}



// ----------------- Part 2 -----------------
// 将错误转换成可选值
func someThrowingFunction() throws -\> Int {
    return 1
    // ...
}
let x = try? someThrowingFunction()
print(x)

// 等价的两种用法
let y: Int?
do {
    y = try someThrowingFunction()
} catch {
    y = nil
}
print(y)

// 有时你知道某个throwing函数实际上在运行时是不会抛出错误的。在这种条件下,你可以在表达式前面写try!来使错误传递失效
let temp = try! someThrowingFunction()
print(temp)

SwiftTypeCasting

Swift属于类型严格的语言,不允许类型自动转换。Swift提供as和is的语法允许进行类型的转换。

import Foundation

// 类型转换在 Swift 中使用 is 和 as 操作符实现
class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}
class Movie: MediaItem {
        var director: String
        init(name: String, director: String) {
            self.director = director
            super.init(name: name)
        }
}
class Song: MediaItem {
        var artist: String
        init(name: String, artist: String) {
            self.artist = artist
            super.init(name: name)
        }
}
// Swift的类型检测器能够推理出 Movie 和 Song 有共同的父类 MediaItem ,所以它推断出 [MediaItem] 类作为 library 的类型。
let library = [
            Movie(name: "Casablanca", director: "Michael Curtiz"),
            Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
            Movie(name: "Citizen Kane", director: "Orson Welles"),
            Song(name: "The One And Only", artist: "Chesney Hawkes"),
            Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]

// 用类型检查操作符( is )来检查一个实例是否属于特定子类型
var movieCount = 0
var songCount = 0
for item in library {
    if item is Movie {
        movieCount++
    }
    if item is Song {
        songCount++
    }
}
print(movieCount, songCount)

// 向下转型,用类型转换操作符(as? 或 as!)
for item in library {
    if let movie = item as? Movie {
        print("Movie: '\(movie.name)', dir. \(movie.director)")
    } else if let song = item as? Song {
        print("Song: '\(song.name)', by \(song.artist)")
    }
}

// AnyObject
let someObjects: [AnyObject] = [
    Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"),
    Movie(name: "Moon", director: "Duncan Jones"),
    Movie(name: "Alien", director: "Ridley Scott")
]

for object in someObjects {
    let movie = object as! Movie
    print("Movie: '\(movie.name)', dir. \(movie.director)")
}

for movie in someObjects as! [Movie] {
    print("Movie: '\(movie.name)', dir. \(movie.director)")
}


// Any
var things = [Any]()
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -\> String in "Hello, \(name)" })

for thing in things {
    switch thing {
    case 0 as Int:
        print("zero as an Int")
    case 0 as Double:
        print("zero as a Double")
    case let someInt as Int:
        print("an integer value of \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("a positive double value of \(someDouble)")
    case is Double:
        print("some other double value that I don't want to print")
    case let someString as String:
        print("a string value of \"\(someString)\"")
    case let (x, y) as (Double, Double):
        print("an (x, y) point at \(x), \(y)")
    case let movie as Movie:
        print("a movie called '\(movie.name)', dir. \(movie.director)")
    case let stringConverter as String -> String:
        print(stringConverter("Michael"))
    default:
        print("something else")
    }
}

SwiftNestedTypes

Swift类型嵌套很容易理解,即Swift允许在类、结构体、枚举内部定义新的类、结构体或枚举。

import Foundation

// 下面这个例子定义了一个结构体 BlackjackCard (二十一点),用来模拟 BlackjackCard 中的扑克牌点数。
// ckCard 结构体包含2个嵌套定义的枚举类型 Suit 和 Rank 。

struct BlackjackCard {
    enum Suit: Character {
        case Spades = "♦️", Hearts = "♥️", Diamonds = "♠️", Clubs = "♣️"
    }
    
    enum Rank: Int {
        case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten
        case Jack, Queen, King, Ace
    
        struct Values {
            let first: Int, second: Int?
        }
    
        var values: Values {
            switch self {
            case .Ace:
                return Values(first: 1, second: 11)
            case .Jack, .Queen, .King:
                return Values(first: 10, second: nil)
            default:
                return Values(first: self.rawValue, second: nil)
            }
        }
    }
    
    // BlackjackCard 的属性和方法
    let rank: Rank, suit: Suit
    
    var description: String {
        var output = "suit is \(suit.rawValue),"
        output += " value is \(rank.values.first)"
        if let second = rank.values.second {
            output += " or \(second)"
        }
        return output
    }
}

let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
print("theAceOfSpades: \(theAceOfSpades.description)")

SwiftExtension

Swift扩展就是向一个已有的类、结构体、枚举类型或者协议类型添加新功能。

import Foundation

// 扩展就是向一个已有的类、结构体、枚举类型或者协议类型添加新功能(functionality)。
// 这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模)
// Swift 中的扩展可以:添加计算型属性和计算型静态属性、定义实例方法和类型方法、提供新的构造器、定义下标
//                  定义和使用新的嵌套类型、使一个已有类型符合某个协议
// 声明一个扩展使用关键字 extension :

// ----------------- Part 1 -------------------------
// 添加计算型实例属性和计算型类型属性
extension Double {
    var km: Double { return self * 1_000.0 }
    var m : Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
    var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")


// ----------------- Part 2 -------------------------
// 添加新的构造器
struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
}
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))

extension Rect {
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))


// ----------------- Part 3 -------------------------
// 添加新的实例方法和类型方法
extension Int {
    func repetitions(task: () -> ()) {
        for _ in 0..<self {
            task()
        }
    }
}
3.repetitions({
        print("Hello!")
        })
// 尾随闭包调用
3.repetitions(){print("World!")}
3.repetitions{print("World!")}


// 可变实例方法
extension Int {
    mutating func square() {
        self = self * self
    }
}
var someInt = 3
someInt.square()
print(someInt)


// ----------------- Part 4 -------------------------
// 向一个已有类型添加新下标
extension Int {
    subscript (var digitIndex: Int)->Int {
        var decimalBase = 1
        while digitIndex > 0 {
            decimalBase *= 10
            --digitIndex
        }
        return (self / decimalBase) % 10
    }
}
print(746381295[0])
print(746381295[1])
print(746381295[9])


// ----------------- Part 5 -------------------------
// 扩展可以向已有的类、结构体和枚举添加新的嵌套类型
extension Int {
    enum Kind {
        case Negative, Zero, Positive
    }
    var kind: Kind {
        switch self {
        case 0:
            return .Zero
        case let x where x > 0:
            return .Positive
        default:
            return .Negative
        }
    }
}
func printIntegerKinds(numbers: [Int]) {
    for number in numbers {
        switch number.kind {
        case .Negative:
            print("- ", terminator: "")
        case .Zero:
            print("0 ", terminator: "")
        case .Positive:
            print("+ ", terminator: "")
        }
    }
    print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])

SwiftProtocol


import Foundation


// 协议定义了一个蓝图,规定了用来实现某一特定工作或者功能所必需的方法和属性。
// 类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。


// ----------------- Part 1 -------------------------
// 规定其遵循者提供特定名称和类型的属性,此外还必须指明是只读的还是可读可写的。
// 如果协议规定属性是可读可写的,那么这个属性不能是常量或只读的计算属性。
// 如果协议只要求属性是只读的(ge ttable),那个属性不仅可以是只读的,如果你代码需要的话,也可以是可写的。

protocol SomeProtocol {
    var mustBeSettable : Int { get set }
    var doesNotNeedToBeSettable: Int { get }
}
protocol AnotherProtocol {
    static var someTypeProperty: Int { get set }
}
protocol FullyNamed {
    var fullName: String { get }
}
struct Person: FullyNamed{
    var fullName: String
}
let john = Person(fullName: "John Appleseed")
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")



// ----------------- Part 2 -------------------------
// 对方法的规定
protocol SomeProtocol {
    static func someTypeMethod()
}
protocol RandomNumberGenerator {
    func random() -> Double
}
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) % m)
        return lastRandom / m
    }
}
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
print("And another one: \(generator.random())")


// ----------------- Part 3 -------------------------
// 对 Mutating 方法的规定
// 用类实现协议中的 mutating 方法时,不用写 mutating 关键字;
// 用结构体,枚举实现协议中的 mutating 方法 时,必须写 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()


// ----------------- Part 4 -------------------------
// 对构造器的规定
// 如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示 required 和 override 修饰符:
protocol SomeProtocol {
    init()
}
class SomeSuperClass {
    init() {
        // 构造器的实现 
    }
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
// 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override" 
    required override init() {
        // 构造器实现
    }
}


// ----------------- Part 5 -------------------------
// 协议是一种类型
class Dice {
    let sides: Int
    let generator: RandomNumberGenerator // 任何遵循了RandomNumberGenerator协议的类型的实例都可以赋值给generator
    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())")
}


// ----------------- Part 6 -------------------------
// 下面的例子是两个基于骰子游戏的协议
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 = [Int](count: finalSquare + 1, repeatedValue: 0)
        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
        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()


// ----------------- Part 7 -------------------------
// 在扩展中添加协议成员
protocol TextRepresentable {
    var textualDescription: String { get }
}
// 通过扩展使得 Dice 类型遵循了一个新的协议
extension Dice: TextRepresentable {
    var textualDescription: String {
        return "A \(sides)-sided dice"
    }
}
let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator())
print(d12.textualDescription)
// SnakesAndLadders 类也可以通过 扩展 的方式来遵循 TextRepresentable 协议
extension SnakesAndLadders: TextRepresentable {
    var textualDescription: String {
        return "A game of Snakes and Ladders with \(finalSquare) squares"
    }
}
print(game.textualDescription)


// ----------------- Part 8 -------------------------
// 通过扩展补充协议声明
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)


// ----------------- Part 9 -------------------------
// 协议类型可以在数组或者字典这样的集合中使用
let things: [TextRepresentable] = [game,d12,simonTheHamster]
// things 数组可以被直接遍历,并打印每个元素的文本表示
for thing in things {
    print(thing.textualDescription)
}


// ----------------- Part 10 -------------------------
// 协议的继承
protocol PrettyTextRepresentable: TextRepresentable {
    var prettyTextualDescription: String { get }
}
// 扩展 SnakesAndLadders ,让其遵循 PrettyTextRepresentable 协议
extension SnakesAndLadders: PrettyTextRepresentable {
    var prettyTextualDescription: String {
        var output = textualDescription + ":\n"
        for index in 1...finalSquare {
            switch board[index] {
                case let ladder where ladder > 0:
                    output += "▲ "
                case let snake where snake < 0:
                    output += "▼ "
                default:
                    output += "○ "
            }
    }
    return output
    }
}
print(game.prettyTextualDescription)


// ----------------- Part 11 -------------------------
// 协议 SomeClassOnlyProtocol 只能被类(class)类型适配。
protocol SomeClassOnlyProtocol: class {
    // 协议定义
}


// ----------------- Part 12 -------------------------
// 协议合成
// 协议合成 并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。
protocol Named {
    var name: String { get }
}
protocol Aged {
    var age: Int { get }
}
struct Person: Named, Aged {
    var name: String
    var age: Int
}
func wishHappyBirthday(celebrator: protocol\<Named, Aged\>) {
    print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(birthdayPerson)


// ----------------- Part 13 -------------------------
// 使用 is 和 as 操作符来检查是否遵循某一协议或强制转化为某一类型。
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 }
}
// Circle, Country, Animal并没有一个相同的基类,然而,它们都是类,它们的实例都可以作为AnyObject类型的变量,存储在同一个数组中
let objects: [AnyObject] = [
    Circle(radius: 2.0),
    Country(area: 243_610),
    Animal(legs: 4)
]
// 对迭代出的每一个元素进行检查,看它是否遵循了HasArea协议
for object in objects {
    if let objectWithArea = object as? HasArea {
        print("Area is \(objectWithArea.area)")
    } else {
        print("Something that doesn't have an area")
    }
}


// ----------------- Part 14 -------------------------
// 协议可以含有可选成员,其遵循者可以选择是否实现这些成员。
// 可选协议只能在含有 @objc 前缀的协议中生效。
// @objc 的协议只能由继承自 Objective-C 类的类或者其他@objc的类来遵循。它也不能被结构体和枚举遵循
@objc protocol CounterDataSource {
    optional func incrementForCount(count: Int) -> Int
    optional var fixedIncrement: Int { get }
}
@objc class Counter: NSObject {
    var count = 0
    var dataSource: CounterDataSource?
    func increment() {
        if let amount = dataSource?.incrementForCount?(count) {
            count += amount
        } else if let amount = dataSource?.fixedIncrement {
            count += amount
        }
    }
}
@objc class ThreeSource: NSObject, CounterDataSource {
    let fixedIncrement = 3
}
var counter = Counter()
counter.dataSource = ThreeSource()
for \_ in 1...4 {
    counter.increment()
    print(counter.count)
}

// 下面是一个更为复杂的数据源TowardsZeroSource
@objc class TowardsZeroSource: NSObject, CounterDataSource {
    func incrementForCount(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)
}


// ----------------- Part 15 -------------------------
// 使用扩展协议的方式可以为遵循者提供方法或属性的实现。
// 可以通过协议扩展的方式来为协议规定的属性和方法提供默认的实现

// 扩展 RandomNumberGenerator 协议,让其提供 randomBool() 方法。该方法使用random()方法返回一个随机的Bool值:
extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
}

print("Here's a random number: \(generator.random())")
print("Here's a random number: \(generator.randomBool())")

// 为协议扩展添加限制条件
extension CollectionType where Generator.Element : TextRepresentable {
    var textualDescription: String {
        let itemsAsText = self.map { $0.textualDescription }
        return "[" + itemsAsText.joinWithSeparator(", ") + "]"
    }
}
let murrayTheHamster = Hamster(name: "Murray")
let morganTheHamster = Hamster(name: "Morgan")
let mauriceTheHamster = Hamster(name: "Maurice")
let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]
print(hamsters.textualDescription)

SwiftGeneric

泛型是 Swift 强大特征中的其中一个,许多 Swift 标准库是通过泛型代码构建出来的。事实上,泛型的使用贯穿了整本语言手册,只是你没有发现而已。例如,Swift 的数组和字典类型都是泛型集。你可以创建一个 Int 数组,也可创建一个 String 数组,或者甚至于可以是任何其他 Swift 的类型数据数组。同样,你也可以创建存储任何指定类型的字典(dictionary),而且这些类型可以是没有限制的。


import Foundation

// 泛型函数
func swapTwoValues<T>(inout a: T, inout \_ b: T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)

// 泛型类型
// 如何写一个泛型集类型Stack(栈)

// 非泛型版本的栈, Int 值型的栈
struct IntStack {
    var items = [Int]()
    mutating func push(item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
}
// 泛型版本
struct Stack<T> {
    var items = [T]()
    mutating func push(item: T) {
    items.append(item)
    }
    mutating func pop() -> T {
    return items.removeLast()
    }
}
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
let fromTheTop = stackOfStrings.pop()

// 扩展一个泛型类型
extension Stack {
    var topItem: T? {
    return items.isEmpty ? nil : items[items.count - 1]
    }
}
if let topItem = stackOfStrings.topItem {
    print("The top item on the stack is \(topItem).")
}

// 类型约束

// 非泛型函数版本
func findStringIndex(array: [String], \_ valueToFind: String) -\> Int? {
    for (index, value) in array.enumerate() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findStringIndex(strings, "llama") {
    print("The index of llama is \(foundIndex)")
}

// 泛型版本
func findIndex\<T: Equatable\>(array: [T], \_ valueToFind: T) -\> Int? {
    for (index, value) in array.enumerate() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}
let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
print(doubleIndex, stringIndex)


// 关联类型  typealias
protocol Container {
    typealias ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}
struct AnotherIntStack<T>: Container {
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    mutating func pop() -> T {
        return items.removeLast()
    }
    // 遵循Container协议的实现 
    typealias ItemType = T
    mutating func append(item: T) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> T {
        return items[i]
    }
}

// Where 语句
extension Array: Container {}

func allItemsMatch\<C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable\>
    (someContainer: C1, anotherContainer: C2) -> Bool {
    if someContainer.count != anotherContainer.count {
        return false
    }
    for i in 0..<someContainer.count {
        if someContainer[i] != anotherContainer[i] {
            return false
        }
    }
    return true
}
var anotherStackOfStrings = AnotherIntStack<String>()
anotherStackOfStrings.push("uno")
anotherStackOfStrings.push("dos")
anotherStackOfStrings.push("tres")
var arrayOfStrings = ["uno", "dos", "tres"]

if allItemsMatch(anotherStackOfStrings, anotherContainer: arrayOfStrings){
    print("All items match.")
} else {
    print("Not all items match.")
}

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

推荐阅读更多精彩内容