Swift學習筆記十二

來源:互聯網
上載者:User

標籤:

方法

方法就是和某種特定類型相關聯的函數。類、結構體、枚舉都可以定義執行個體方法和類型方法。類型方法和OC中的類方法類似。

結構體和枚舉也可以定義方法是Swift與C/OC之間很大的一個區別,在OC中,只有類才能定義方法。

執行個體方法

執行個體方法是從屬於某個類執行個體或結構體執行個體、枚舉執行個體的方法。他們提供與該執行個體相關的功能,比如擷取更改屬性或者提供其他函數功能。執行個體方法的文法和函數完全相同。

執行個體方法隱式地可以訪問該類型的屬性和方法。只能從該類型的執行個體上調用執行個體方法。比如:

class Counter {    var count = 0    func increment() {        count++    }    func incrementBy(amount: Int) {        count += amount    }    func reset() {        count = 0    }}let counter = Counter()// the initial counter value is 0counter.increment()// the counter‘s value is now 1counter.incrementBy(5)// the counter‘s value is now 6counter.reset()// the counter‘s value is now 0

 

方法的內部和外部參數名稱

如前所述,函數的參數可以同時擁有內部名稱和外部名稱,方法也是如此。不過,執行個體方法的內部和外部名稱的預設行為和函數有所不同。

像OC一樣,Swift中方法的名字一般都是用一個介詞如with、for、by和第一個參數名稱接應起來的。比如上個例子中的incrementBy(_:),介詞使得方法在被調用的時候可讀性更高,就像一個連貫的句子一樣。因此Swift通過給方法參數提供了不同於函數的預設行為來使得這種方法命名方式更加便利:Swift給方法的第一個參數名提供了內部參數名,給第二個及以後的所有參數名同時提供了內部和外部參數名。

依舊以上一個Counter為例子,假設它的執行個體方法incrementBy需要兩個參數:

class Counter {    var count: Int = 0    func incrementBy(amount: Int, numberOfTimes: Int) {        count += amount * numberOfTimes    }}

預設情況下,Swift把第一個參數名amount只看做內部參數名,而把numberOfTimes同時看做內部和外部變數名。因此,在調用這個執行個體方法的時候:

let counter = Counter()counter.incrementBy(5, numberOfTimes: 3)// counter value is now 15

不需要給方法的第一個參數定義外部參數名,因為它的意義通過方法名incrementBy可以很明確地表現出來,但是後邊的參數都需要外部參數名使得在方法調用的時候語義非常明確。這種預設的處理方式就像你在方法定義的時候已經在第二個及以後的每個參數名稱前加上了(#)符號。

當然,如果你希望改變這種預設行為,給第一個參數指定外部參數名,也可以用通常的方法指定,如果你不想給第二個及以後的參數添加預設外部參數名,那麼可以在參數名前加上底線(_)作為外部參數名。

 

self屬性

每個執行個體對象都有一個隱式屬性self,它指向執行個體對象本身。在實際開發中,並不需要經常寫self,在執行個體方法內部,如果你不顯示地寫明self,每次你在使用一直的屬性或方法時,Swift都假定你是在用當前對象的屬性或方法。這個原則的一個主要例外是當方法的參數名和執行個體對象的屬性名稱相同時,方法的參數名會具有較高的優先順序,如果需要使用執行個體對象的屬性,就需要使用self。

 

從執行個體方法內改變實值型別

結構體和枚舉都是實值型別的,預設情況下,實值型別的屬性是不能從它的執行個體方法內部改變的。

但是,如果你希望從一個特殊的方法裡邊改變結構體或枚舉的屬性值,你可以選擇改變那個方法的行為。然後方法就可以從內部改變它的屬性,並且它產生的任何改變在方法結束後都會寫到原始的結構體上。方法還可以給它的隱式屬性self賦一個全新的執行個體對象,當方法結束時,這個新執行個體將取代現有的那個。

通過在func前加上關鍵字mutating來實現上面的行為:

struct Point {    var x = 0.0, y = 0.0    mutating func moveByX(deltaX: Double, y deltaY: Double) {        x += deltaX        y += deltaY    }}var somePoint = Point(x: 1.0, y: 1.0)somePoint.moveByX(2.0, y: 3.0)println("The point is now at (\(somePoint.x), \(somePoint.y))")// prints "The point is now at (3.0, 4.0)”

上面的樣本中,定義了一個mutating方法moveByX,它的目的是將點對象移動一定距離,它是在當前的點對象上直接操作的,而不是返回一個新的點對象。

注意,如果你將實值型別執行個體對象賦值給了一個常量,那你就不能調用它的mutating方法了,因為一旦賦值給常量,它的所有屬性就都不能改變了,即使該屬性是變數屬性。

 

從Mutating方法內部給self賦值

Mutating方法可以給隱式屬性self賦一個全新的對象,比如上面的例子可以改寫成:

struct Point {    var x = 0.0, y = 0.0    mutating func moveByX(deltaX: Double, y deltaY: Double) {        self = Point(x: x + deltaX, y: y + deltaY)    }}

這個方法是返回了一個全新點對象執行個體,不過當方法完成時,結果和前面的例子是完全一樣的。

枚舉類型的Mutating方法可以將隱式屬性self設定為這個枚舉的另一個不同的成員:

enum TriStateSwitch {    case Off, Low, High    mutating func next() {        switch self {        case Off:            self = Low        case Low:            self = High        case High:            self = Off        }    }}var ovenLight = TriStateSwitch.LowovenLight.next()// ovenLight is now equal to .HighovenLight.next()// ovenLight is now equal to .Off

 

類型方法

如前所述,執行個體方法就是在執行個體對象上調用的方法。而類型方法是指從類型本身調用的方法。通過在func前面加上關鍵字static來宣告類型方法。類也可以用class關鍵字來允許子類可以重載父類對該方法的實現。

在OC中,只能在類上宣告類型方法,而在Swift中,可以在類、結構體、枚舉上宣告類型方法。各種類型方法都顯示地標明它支援的類型。

class SomeClass {    class func someTypeMethod() {        // type method implementation goes here    }}SomeClass.someTypeMethod()

在類型方法內部,隱式屬性self指向類型本身,而非它的執行個體對象。對結構體和枚舉而言,這意味著當類型屬性名稱和方法參數名相同的時候可以用來做區分。

通常情況下,在類型方法裡邊出現的未定義的方法和屬性名稱字都會指向這個類型的類型屬性和類型方法,不需要在屬性和方法名字前加類型名稱首碼。

 

下標

下標是擷取集合、列表或者隊列中元素的簡便方法,下標可以通過索引序列(index)來擷取和儲存值,而不需要額外擷取和設定方法。

類、結構和枚舉可以定義下標,你可以為一個類型定義多個下標,下標也不局限於一維,可以定義具有多個參數的下標。

下標文法

下標文法通過在執行個體名稱後面寫上包含一個或多個值的中括弧來使你可以查詢執行個體對象。文法和執行個體方法文法及計算式屬性文法都很像。通過subscript關鍵字來定義下標,然後像執行個體方法那樣指定一個或多個輸入參數及傳回型別。和執行個體方法不同的是,下標可以是唯讀活著是讀寫的。這個行為和計算式屬性的getter和setter相似:

subscript(index: Int) -> Int {    get {        // return an appropriate subscript value here    }    set(newValue) {        // perform a suitable setting action here    }}

這個setter和計算式屬性類似的,如果你指定了新值參數名,則用那個名字,如果沒有指定,預設提供的是newValue。

如果是唯讀下標,就去掉setter,和計算式屬性類似,只有getter時可以省略get關鍵字用簡寫形式。

struct TimesTable {    let multiplier: Int    subscript(index: Int) -> Int {        return multiplier * index    }}let threeTimesTable = TimesTable(multiplier: 3)println("six times three is \(threeTimesTable[6])")// prints "six times three is 18”

下標可以接受任意數目的任意類型的參數,也可以返回任意類型的傳回值。下標可以使用變數參數以及可變參數,但是不能使用in-out參數,也不能給參數提供預設值。

類和結構體可以根據需要提供多種下表實現方式。至於哪種方式會被使用,取決於在使用下標的時候,方括弧內參數類型,這也叫做下標重載。

下標通常都是接受一個參數,當然,也可以定義為接受多個參數:

struct Matrix {    let rows: Int, columns: Int    var grid: [Double]    init(rows: Int, columns: Int) {        self.rows = rows        self.columns = columns        grid = Array(count: rows * columns, repeatedValue: 0.0)    }    func indexIsValidForRow(row: Int, column: Int) -> Bool {        return row >= 0 && row < rows && column >= 0 && column < columns    }    subscript(row: Int, column: Int) -> Double {        get {            assert(indexIsValidForRow(row, column: column), "Index out of range")            return grid[(row * columns) + column]        }        set {            assert(indexIsValidForRow(row, column: column), "Index out of range")            grid[(row * columns) + column] = newValue        }    }}

 var matrix = Matrix(rows: 2, columns: 2)

matrix[0, 1] = 1.5

 matrix[1, 0] = 3.2

 

繼承

一個類可以從另一個類繼承方法,屬性或者其他特性,繼承的類叫子類,被繼承的類叫超類。繼承是類區別於其他類型的一個主要特徵。

類可以訪問它超類的屬性、方法和下標,也可以提供自己對這些屬性、方法和下標的重載版本來改變它們的行為。類也可以給繼承的屬性添加屬性觀察者,無論這個屬性在超類中是儲存式屬性還是計算式屬性。

任何不繼承自其他類的類被稱為基類。Swift中的類並沒有統一繼承自某個全域基類,如果你建立的類沒有繼承自任何類,它就是基類。

class Vehicle {    var currentSpeed = 0.0    var description: String { //這是一個唯讀計算式屬性        return "traveling at \(currentSpeed) miles per hour"    }    func makeNoise() {        // do nothing - an arbitrary vehicle doesn‘t necessarily make a noise    }}

這裡定義了一個沒有繼承自任何類的基類,它的一個執行個體方法並沒有實現,這是留給子類去具體實現的。

子類的定義是在類名後邊加上冒號,然後加上超類的名稱:

class Bicycle: Vehicle {    var hasBasket = false}

子類預設就繼承了超類的特性,比如Bicycle類就自動有currentSpeed屬性及makeNoise方法等。當然,子類還可以定義自己的屬性和方法,子類也可以被再次繼承,子類會依次獲得繼承鏈上那些超類的特性。

 

重載

子類可以給繼承來的執行個體方法、類型方法、執行個體屬性、類型屬性及下標提供自己的實現版本,否則它們的實現方式就是繼承自超類的。這叫做重載。

在重載實現前面加上關鍵字override來標明重載某個特性,這表示你是有意要重載某個特性,而不是不小心用了同樣的名稱或定義。事實上,任何不帶override關鍵字的重載都會在編譯時間觸發錯誤。override關鍵字也會讓Swift檢查重載是否和超類中的特性匹配,以確保重載的定義是正確的。

有時候,在子類重載超類特性的時候,可能需要訪問超類對該類的實現,這通過super首碼來訪問超類的屬性、方法及下標實現。

重載方法就是在方法的func前加上override關鍵字.

任何繼承來的屬性都可以被重載,你可以提供自訂的getter,需要的時候也可以提供自訂的setter,無論這個屬性在超類中是定義為儲存屬性或是計算屬性。超類中屬性究竟是儲存式還是計算式子類是無法知道的,它只知道這個屬性的名字和類型。你必須同時寫明重載的屬性的名稱和類型以便Swift檢查重載是否正確。你可以通過提供getter和setter將一個唯讀屬性在子類中重載為讀寫的屬性,然後,你不能將一個可讀寫的屬性重載為唯讀。

注意:如果你重載的時候提供了setter,那麼你必須同時提供getter作為重載的一部分。

class Car: Vehicle {    var gear = 1    override var description: String {        return super.description + " in gear \(gear)"    }}

你可以通過屬性重載來給繼承來的屬性添加觀察者,無論這個屬性最初是如何?和定義的,在它發生改變的時候你都可以收到通知。

注意:你不能給繼承來的常量儲存屬性或者唯讀計算屬性添加觀察者,這些屬性是不能被改變的,因此在重載時提供willSet和didSet是不合適的。並且,你也不能同時重載一個屬性的setter和它的觀察者,因為如果你重載了setter,你可以直接在setter裡邊實現觀察者的目的。

class AutomaticCar: Car {    override var currentSpeed: Double {        didSet {            gear = Int(currentSpeed / 10.0) + 1        }    }}let automatic = AutomaticCar()automatic.currentSpeed = 35.0println("AutomaticCar: \(automatic.description)")// AutomaticCar: traveling at 35.0 miles per hour in gear 4

 

阻止重載

你可以通過標明最終版本來阻止方法、屬性或下標被重載,在他們關鍵字前加上final關鍵字即可,比如:final var, final func, final class func, final subscript等。

任何試圖重載已經標記為final的特性都會觸發編譯時間錯誤。

你也可以在類關鍵字class前加上final關鍵字來將整個類標記為最終版本,任何試圖繼承final類都會觸發編譯時間錯誤。

Swift學習筆記十二

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.