Swift學(xué)習(xí)筆記(十一)--拓展和協(xié)議

終于到了最讓人激動(dòng)的特性了, 拓展和協(xié)議是Swift里面的一大亮點(diǎn), 可以說會(huì)改變目前我們寫代碼的一些方式(思維還是一致的). 雖然很重要且給力, 但是內(nèi)容還是不難的, 只需要記住一些概念即可.

拓展

拓展其實(shí)和ObjC里面的category很想(貌似ObjC里面的匿名category也叫拓展, 但是感覺功能比較弱). Swift里面的拓展被增強(qiáng)了很多, 可以擴(kuò)展類, 結(jié)構(gòu)體, 枚舉,甚至是協(xié)議, 可以做這么些事情:
1). 增加運(yùn)算屬性和類運(yùn)算屬性(只能是運(yùn)算屬性, 不能是存儲(chǔ)屬性, 因?yàn)? 如果增加存儲(chǔ)屬性, 原來的類的構(gòu)造器是沒有初始化它們的.)
2). 定義實(shí)例方法和類方法
3). 提供新的構(gòu)造器(只能是便利構(gòu)造器)
4). 定義下標(biāo)
5). 定義和使用聚合類型
6). 使已有的類型實(shí)現(xiàn)協(xié)議

可以看到, 原生類里能做的大多數(shù)事情, 拓展里面都可以做, 是一種增強(qiáng)原始類能力的一大利器. 更有意思的是, extension還能擴(kuò)展協(xié)議, 提供協(xié)議的實(shí)現(xiàn), 這個(gè)具體在協(xié)議部分再講. 總之, 擴(kuò)展是Swift提供的一個(gè)強(qiáng)大的功能, 可以改變我們目前很多的代碼結(jié)構(gòu). 例如, 看了objc.io里面的第一章, 更輕量的viewcontrollerss就會(huì)知道, 在ObjC中, viewcontroller容易被寫的很重, 很多的UI, 業(yè)務(wù)邏輯, 響應(yīng)邏輯被堆在viewcontroller中, 所以一般我們都會(huì)拆分掉, 比如最tableview的datasource會(huì)另寫一個(gè)類來實(shí)現(xiàn). 但是有了extension, 我們直接擴(kuò)展viewcontroller就好了, 讓擴(kuò)展部分來承載datasource的實(shí)現(xiàn).

需要注意的是, 拓展可以新增功能, 但是不能重載功能. 同時(shí), 一旦定義了拓展, 那么這些拓展的功能將對(duì)所有的該類型實(shí)例生效, 即使創(chuàng)建在定義拓展之前的.

擴(kuò)展的語法

直接看例子吧:

// 擴(kuò)展類型:
extension SomeType {
    // new functionality to add to SomeType goes here
}
// 實(shí)現(xiàn)協(xié)議:
extension SomeType: SomeProtocol, AnotherProtocol {
    // implementation of protocol requirements goes here
}

運(yùn)算屬性

直接看官方給出對(duì)Double的運(yùn)算屬性拓展, 給Double加上了各種長(zhǎng)度單位:

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")
// prints "One inch is 0.0254 meters"
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// prints "Three feet is 0.914399970739201 meters"
let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// prints "A marathon is 42195.0 meters long"

構(gòu)造器

開篇提到了extension只能拓展便利構(gòu)造器, 這是因?yàn)? 在構(gòu)造器里面, 我們知道繼承構(gòu)造器的條件, 其中第二個(gè)條件是"子類提供了全部的父類指派構(gòu)造器而不是從情況1獲得的, 即使是提供了一部分實(shí)現(xiàn), 那么它將自動(dòng)繼承所有的父類便利構(gòu)造器", 如果extension拓展了指派構(gòu)造器, 那么該類的繼承者就無法繼承便利構(gòu)造器了, 之前調(diào)用的地方自然就都出錯(cuò)了.
值得一說的是, 析構(gòu)器也只能在原始類中定義和實(shí)現(xiàn), 畢竟, extension不能有存儲(chǔ)屬性, 也就沒什么需要釋放資源的情況了.

提醒一下, 之前也在構(gòu)造器那章說過, 如果值類型為每個(gè)屬性都提供了默認(rèn)值并且沒有實(shí)現(xiàn)任何自定義的構(gòu)造器, Swift會(huì)為之生成默認(rèn)構(gòu)造器. 如果我們?cè)趀xtension里面新增了一個(gè)構(gòu)造器, Swift不會(huì)把默認(rèn)構(gòu)造器去掉(不然以前調(diào)用的地方不就出錯(cuò)了么...)

看一個(gè)例子來吧:

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

// 拓展構(gòu)造器
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)
    }
}
// 使用新增的構(gòu)造器
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
    size: Size(width: 3.0, height: 3.0))

方法

直接根據(jù)例子來講吧, 比如官方拓展了Int, 讓Int可以重復(fù)執(zhí)行閉包:

extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self {
            task()
        }
    }
}
// 打印3次Hello
3.repetitions({
    print("Hello!")
})
// 之前提過的簡(jiǎn)化寫法:
3.repetitions {
    print("Goodbye!")
}

拓展增加的方法也是可以修改實(shí)例本身或者一些屬性的, 當(dāng)然, 改變值類型屬性的函數(shù)還是要加mutating的. 例如:

extension Int {
    mutating func square() {
        self = self * self
    }
}
var someInt = 3
someInt.square()  // someInt為9

下標(biāo)

一開始也說了可以在extension中拓展下標(biāo), 但是還是要記住不能重載已有的下標(biāo)運(yùn)算符. 直接看例子:

// 這個(gè)奇怪的例子返回的是Int型數(shù)據(jù)中, 從右往左數(shù)的第幾個(gè)數(shù)字是幾
extension Int {
    subscript(var digitIndex: Int) -> Int {
        var decimalBase = 1
        while digitIndex > 0 {
            decimalBase *= 10
            --digitIndex
        }
        return (self / decimalBase) % 10
    }
}
746381295[0]
// returns 5
746381295[1]
// returns 9
746381295[2]
// returns 2
746381295[8]
// returns 7

聚合類型

還是直接看例子吧, 并沒有太多新的東西, 或者直接跳過不看也可以:

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])
打印出 + + - 0 - 0 +

很有用的一章, 而且知識(shí)點(diǎn)也不難, 具體細(xì)節(jié)可以參考官方文檔

協(xié)議

協(xié)議在Objc里面也有, 一般會(huì)被類比為Java里的接口或者C++中的純虛類, 不管是協(xié)議還是接口, 其本質(zhì)都是定義你要被外界所影響的方式.

Swift2.0發(fā)布后, Swift的編程的主要思想(或者說范式)就轉(zhuǎn)向了面向協(xié)議編程(不僅僅只有面向協(xié)議). 面向?qū)ο?OOP)和面向協(xié)議(POP)的編程思想最大的區(qū)別就如其名所描述, 前者把世界萬物都看做一個(gè)個(gè)對(duì)象, 后者則不一樣, 不在乎世界萬物的本質(zhì), 只在乎你是否能做某些事件. 舉個(gè)例子來說明其中的區(qū)別吧:
我們想讓所有的類都實(shí)現(xiàn)某個(gè)方法, 如description, 打印出可以描述當(dāng)前對(duì)象的信息, 如果是
OOP, 我們很自然的想到寫一個(gè)基類, 提供默認(rèn)實(shí)現(xiàn)的description方法, 正如NSObject所做的一樣, 但是POP則會(huì)先聲明一個(gè)Printable的協(xié)議, 然后用extension來提供默認(rèn)實(shí)現(xiàn)(Swift2.0之后才可以對(duì)協(xié)議extension, 所以才說2.0之后才是POP), 之后每一個(gè)類,結(jié)構(gòu)體或者枚舉, 只要聲明實(shí)現(xiàn)了這個(gè)協(xié)議, 就可以使用這個(gè)方法了. 這就是二者的區(qū)別, 所以, 在切換到Swift之后, 我們的編程思想也要發(fā)生相應(yīng)的變化, 而不是沿襲之前面向?qū)ο蟮乃伎挤绞?

上面的話都說明了協(xié)議在Swift里的重要性, 里面包含的細(xì)節(jié)比較多, 官方文檔給出的也都是相對(duì)基礎(chǔ)的內(nèi)容, 有機(jī)會(huì)的話會(huì)嘗試更多的有關(guān)于協(xié)議的內(nèi)容, 然后分享出來.

先進(jìn)入?yún)f(xié)議的世界吧.

協(xié)議語法

protocol SomeProtocol {
    // protocol definition goes here
}
// 協(xié)議可以被類,結(jié)構(gòu)體和枚舉實(shí)現(xiàn)
struct SomeStructure: FirstProtocol, AnotherProtocol {
    // structure definition goes here
}
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
    // class definition goes here
}
// 但是如果這么寫就只能被類實(shí)現(xiàn)了, 沒有只能被struct和enum實(shí)現(xiàn)的協(xié)議
protocol ClassOnlyProtocol : class{  // <-- 加上: class
    // class only protocol definition goes here
}

需要提一句的是, Objc的協(xié)議是弱要求, 不實(shí)現(xiàn)只會(huì)報(bào)警, 但是Swift里面是強(qiáng)要求, 不實(shí)現(xiàn)直接編譯不通過.

屬性和方法要求

協(xié)議可以要求其實(shí)現(xiàn)者擁有某些屬性和方法, 還可以定制要求其提供get和set方法, 如:

protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
    static var someTypeProperty: Int { get set }  // <-- 類屬性

}
protocol RandomNumberGenerator {
    func random() -> Double
}

可變方法要求

因?yàn)閰f(xié)議既可以被類實(shí)現(xiàn), 又可以被結(jié)構(gòu)體和枚舉實(shí)現(xiàn), 所以如果要求實(shí)現(xiàn)的某些方法里面修改了結(jié)構(gòu)體或枚舉自身或者屬性的值(參考之前的筆記五), 那么就需要加上mutating(如果不想結(jié)構(gòu)體繼承, 就顯式如協(xié)議語法小節(jié)所說加上: class).
如果協(xié)議沒有加上mutating但是實(shí)現(xiàn)的結(jié)構(gòu)體或者枚舉自行加上, 則會(huì)報(bào)錯(cuò), 認(rèn)為沒有實(shí)現(xiàn), 同時(shí), 如果自己再加上一個(gè)不加mutating的方法, 則認(rèn)為重復(fù)命名方法了, 個(gè)人感覺這里處理的相當(dāng)矛盾...
總之語法如此, 所以在寫protocol方法的時(shí)候, 如果要給結(jié)構(gòu)體和枚舉用, 就在方法前加上mutating

構(gòu)造器要求

和方法一樣, 協(xié)議中也可以要求實(shí)現(xiàn)者實(shí)現(xiàn)特定的構(gòu)造器. 如:

protocol SomeProtocol {
    init(someParameter: Int)
}
// 實(shí)現(xiàn)者可以把這個(gè)構(gòu)造器實(shí)現(xiàn)為指定構(gòu)造器或者便利構(gòu)造器, 但是必須要加上required修飾符
class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        // initializer implementation goes here
    }
}
// 用required修飾符后, 其子類也必須要有這么一個(gè)構(gòu)造器(如果本類用final修飾之后, 就不需要用required來修飾了, 因?yàn)檫@個(gè)類已經(jīng)不能被繼承了), 參考筆記七--構(gòu)造器;
// 如果子類實(shí)現(xiàn)協(xié)議指定的構(gòu)造器與父類重名了, 那么required和override都必須寫上.
// 進(jìn)一步來看, 協(xié)議也可以要求實(shí)現(xiàn)可失敗的構(gòu)造器, 同樣具體參考筆記七.

把協(xié)議當(dāng)做類型

這個(gè)概念在Swift里面比較重要, 后面的幾個(gè)小節(jié)都可以基于這一點(diǎn)來理解. 與ObjC不一樣, 協(xié)議并不能獨(dú)立成為一個(gè)類型, 一般都會(huì)和id合用, 例如: id<UITableViewDataSource>
協(xié)議在很多場(chǎng)合下和類型是差不多的, 例如:
1). 在函數(shù), 方法或者構(gòu)造器中被當(dāng)做參數(shù)類型, 返回值類型
2). 用作常量, 變量或者屬性的類型
3). 用作數(shù)組, 字典或者其它容器內(nèi)部對(duì)象的類型
正因?yàn)槿绱? 所以協(xié)議命名的時(shí)候也要求以大寫開頭.

看看官方的例子:

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())")
}
// 這里再次強(qiáng)調(diào), 面向協(xié)議不在乎你是什么, 只在乎你能做什么, 著名的鴨子理論對(duì)解釋面向協(xié)議很有用:"如果一個(gè)動(dòng)物像鴨子一樣游泳,像鴨子一樣叫,就把它當(dāng)做鴨子".

代理

在ObjC里面, 協(xié)議在代理上面用的范圍十分廣泛, 在Swift中同樣如此, 直接看官方的例子:

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)
}
// 后面的實(shí)現(xiàn)很長(zhǎng), 直接截取使用代理的部分:
...
var delegate: DiceGameDelegate?   // <--- 聲明一個(gè)代理的對(duì)象
func play() {
delegate?.gameDidStart(self) 
...
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
...
delegate?.gameDidEnd(self)
}
// Optional Chain在這里用的簡(jiǎn)直不能舒服了...
// 下面是代理的實(shí)現(xiàn)類
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")
    }
}

用拓展添加協(xié)議的一致性

Swift2.0允許我們隊(duì)協(xié)議本身進(jìn)行拓展, 即便沒有權(quán)限來訪問源代碼. 當(dāng)然, 這要求你必需提供默認(rèn)實(shí)現(xiàn)(不提供的話, 原來的代碼不就編譯不過了么), 如:

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

有一種情況比較特殊, 就是實(shí)現(xiàn)者本身就實(shí)現(xiàn)了某個(gè)協(xié)議, 但是它并沒有聲明說實(shí)現(xiàn)這個(gè)協(xié)議, 那么我們可以用extension幫它聲明, 提供空實(shí)現(xiàn)即可, 如:

struct Hamster {
    var name: String
    var textualDescription: String {
        return "A hamster named \(name)"
    }
}
extension Hamster: TextRepresentable {}

這種情況在用別人的代碼的時(shí)候可能會(huì)出現(xiàn), 如果用別人用OOP寫代碼, 自己用POP寫, 函數(shù)傳參都是用協(xié)議類型, 那么不這么做就會(huì)報(bào)錯(cuò).

集合中的協(xié)議類型

可塞到某個(gè)集合中的對(duì)象一般要加一些限定條件, 比如[Int],[String]等, 也可以是更加寬泛的條件, 比如[AnyObject]等. 既然協(xié)議可以當(dāng)做為類型, 那么就可以作為集合的限定條件.如:

let things: [TextRepresentable] = [game, d12, simonTheHamster]

協(xié)議繼承和組合

和ObjC一樣, 協(xié)議也可以繼承, 不過不同的是, 并不要求每個(gè)協(xié)議都必須繼承一個(gè)根協(xié)議.
大概是這么樣的, 并沒有什么區(qū)別:

protocol PrettyTextRepresentable: TextRepresentable {
    var prettyTextualDescription: String { get }
}

組合就好玩多了, 可以把多個(gè)協(xié)議組合成一個(gè), 這只是一個(gè)語法糖而已, 并不會(huì)多造出一個(gè)新的協(xié)議, 如:

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)

用上typealias一起用可以讓可讀性更好, 如:

typealias SomeTextProtocol = protocol<SomeProtocol, TextRepresentable>

檢查協(xié)議一致性

如上面的小節(jié)所說, 協(xié)議是可以看做類型的, 所以也可以自然會(huì)面對(duì)筆記十中所要面對(duì)的類型轉(zhuǎn)換和檢查的問題. 同樣, 協(xié)議的檢查和轉(zhuǎn)換也是用is和as來完成的:
1). is操作符在實(shí)例實(shí)現(xiàn)了協(xié)議的時(shí)候返回true, 否則返回false
2). as?操作符返回可選的協(xié)議類型值, 如果該實(shí)例沒有實(shí)現(xiàn)協(xié)議則返回nil
3). as!操作符強(qiáng)制轉(zhuǎn)換為目標(biāo)協(xié)議類型, 如果失敗則會(huì)造成runtime error
如果對(duì)筆記十的類型章節(jié)理解透徹的話, 這邊也差不多, 直接看例子吧:

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 {  // <-- 沒有實(shí)現(xiàn)HasArea協(xié)議
    var legs: Int
    init(legs: Int) { self.legs = legs }
}
// 構(gòu)造數(shù)組
let objects: [AnyObject] = [
    Circle(radius: 2.0),
    Country(area: 243_610),
    Animal(legs: 4)
]
// 遍歷數(shù)組
for object in objects {
    if let objectWithArea = object as? HasArea {
        print("Area is \(objectWithArea.area)")
    } else {
        print("Something that doesn't have an area")  // <--Animal的實(shí)例會(huì)執(zhí)行到這里
    }
}

可選的協(xié)議要求

和ObjC里面一樣, 可以要求某些方法是可選的, 這些方法不要求協(xié)議的實(shí)現(xiàn)者必須實(shí)現(xiàn), 如UITableViewDataSource里面的numberOfSectionsInTableView:方法, 如果不實(shí)現(xiàn)默認(rèn)會(huì)返回1.
在Swift里面, 如果一個(gè)方法或者屬性被標(biāo)記為optional之后, 其類型也會(huì)被轉(zhuǎn)換為optional. 這其實(shí)很好例子, 畢竟對(duì)于實(shí)現(xiàn)者來說, 這個(gè)值可能存在也可能不存在, 自然就會(huì)是optional類型的了.
需要注意的是, 如果協(xié)議里面使用了optional修飾符, 就必須要用@objc來修飾整個(gè)協(xié)議, 例如:

@objc protocol CounterDataSource {
    optional func incrementForCount(count: Int) -> Int
    optional var fixedIncrement: Int { get }
}

具體的要去看另外一份文檔---- Using Swift with Cocoa and Objective-C (Swift 2.1), 這個(gè)有機(jī)會(huì)也一起聊聊里面有意思的東西, 畢竟在很長(zhǎng)的一段時(shí)間內(nèi), Objc和Swift的代碼都會(huì)共存.
另外, 如果一個(gè)協(xié)議用@objc修飾了, 那么它只能被從ObjC中繼承的類和其它被@objc修飾的類所實(shí)現(xiàn)了.

下面一個(gè)例子用了optional chaining來訪問optional protocol的屬性和方法:

@objc protocol CounterDataSource {
    optional func incrementForCount(count: Int) -> Int
    optional var fixedIncrement: Int { get }
}
class Counter {
    var count = 0
    var dataSource: CounterDataSource?
    func increment() {
        if let amount = dataSource?.incrementForCount?(count) {
            count += amount
        } else if let amount = dataSource?.fixedIncrement {
            count += amount
        }
    }
}
// 實(shí)現(xiàn)類
class ThreeSource: NSObject, CounterDataSource {
    let fixedIncrement = 3
}
// 使用
var counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
    counter.increment()
    print(counter.count)
}

官方文檔上還有另外一個(gè)例子, 有興趣可以去看看.

拓展協(xié)議

如開篇所說, Swift2.0已經(jīng)可以拓展協(xié)議了, 拓展的協(xié)議里的新屬性和新方法必須提供默認(rèn)實(shí)現(xiàn)(所以, 屬性的set是沒法拓展了). 關(guān)于默認(rèn)實(shí)現(xiàn), 如果extension里面的默認(rèn)實(shí)現(xiàn)在原本的實(shí)現(xiàn)者里面就已經(jīng)有了, 那么以實(shí)現(xiàn)者本身的為準(zhǔn).

為協(xié)議拓展添加約束

有時(shí)候, 我們想拓展一個(gè)協(xié)議, 但是需要加以一定的限制, 例如還需要滿足另外一個(gè)協(xié)議的情況下才允許使用此拓展, 就需要用到約束了. 約束通過where來添加, 這個(gè)在下一章Generic里面會(huì)講, 以一個(gè)例子來說明:

// Generator.Element就是集合內(nèi)部的元素, 所以集合起來就是, 當(dāng)集合內(nèi)部元素實(shí)現(xiàn)了TextRepresentable協(xié)議的時(shí)候, 可以使用這個(gè)拓展
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) // <--打印 "[A hamster named Murray, A hamster named Morgan, A hamster named Maurice]"

需要提一句的是, 如果需要滿足多個(gè)條件, 用逗號(hào)(,)分隔開, 或者用上面說的protocol組合形式. 暫未發(fā)現(xiàn)可以用或(||)條件來滿足的, 如果有, 會(huì)更新到這里的.

協(xié)議的基礎(chǔ)就到這里, 具體細(xì)節(jié)參考官方文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容