十二 下標swift
1、下標語法 數組
下標可讓你經過實例名後加中括號內一個或多個數值的形式檢索一個元素。語法和方法語法和屬性語法相似,經過使用subscript關鍵定義,一個或多個輸入參數以及一個返回值。不一樣於實例方法的是,下標能夠是可讀寫的或者只讀的。這種行爲經過一個getter和setter語句聯通,就像是計算屬性同樣。app
subscript(index: Int) -> Int { get { return an appropriate subscript value here } set(newValue) { perform a suitable setting action here } }
newValue的類型和下標返回的類型同樣。和計算屬性同樣,你能夠選擇不指定setter的參數,由於當你不指定的時候,默認參數newValue會被提供給setter。ide
和計算屬性同樣,只讀下標能夠不須要get關鍵詞函數
下面是一個只讀下標的實現,定義了一個TimesTable結構來表示一個整數的倍數表:工具
在這個例子中,實例TimesTable被建立爲3倍數表,這是經過在初始化的時候爲multiplier參數傳入的數值3設置的。ui
注意:編碼
倍數表是根據特定的數學規則設置的,因此不該該爲threeTimeTable[someIndex]元素設置一個新值,因此TimesTable的下標定義爲只讀。spa
struct TimesTable { let multiplier: Int subscript(index: Int) -> Int { return multiplier * index } } let threeTimesTable = TimesTable(multiplier: 3) print("six times three is \(threeTimesTable[6])")
2、下標的使用orm
下標的具體含義由使用它時的上下文來肯定。下標主要用來做爲集合,列表和序列的元素快捷方式。你能夠自由的爲你的類或者結構定義你所須要的下標。
好比說,Swift中字典類型實現的下標是設置和檢索字典實例中的值。能夠經過分別給出下標中的關鍵詞和值來設置多個值,也能夠經過下標來設置單個字典的值:
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] numberOfLegs["bird"] = 2
上面的例子中定義了一個變量numberOfLegs,而後經過鍵值對初始化。numberOfLegs的類型是字典類型Dictionary<String, Int>。在字典建立以後,例子使用了下標賦值方法添加了一個類型爲字符串的鍵」bird」和Int值2到字典中。
注意:
Swift中字典類型實現的鍵值對下標是可選類型。對於numberOfLges字典來講,返回的值是Int?,也就是可選Int值。字典的這種使用可選類型下標的方式說明不是全部的鍵都有對應的值。一樣也能夠經過給鍵賦值nil來刪除這個鍵。
3、下標選項
下標能夠接收任意數量的參數,參數的類型也能夠各異。下標還能夠返回任何類型的值。下標可使用變量參數或者可變參數,可是不可以使用輸入輸出參數或者提供默認參數的值。
類或者結構能夠根據須要實現各類下標方式,能夠在須要的時候使用合適的下標經過中括號中的參數返回須要的值。這種多下標的定義被稱做下標重載。
固然,最多見的下標用法是單個參數,也能夠定義多個參數的下標。下面的例子演示了一個矩陣Matrix結構,它含有二維的Double值。矩陣結構的下標包括兩個整形參數:
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 } } }
矩陣Matrix提供了一個初始化方法,使用兩個參數rows和columns,而後創建了一個數組來存儲類型爲Double的值rows*columns。每一個矩陣中的位置都被設置了一個初始值0.0。經過傳遞初始值0.0和數組長度給數組初始化方法完成上述操做
你能夠傳遞兩個參數row和column來完成Matrix的初始化:
var matrix = Matrix(rows: 2, columns: 2)
矩陣中的值能夠經過使用包含row和column以及逗號的下標來設置:
matrix[0, 1] = 1.5 matrix[1, 0] = 3.2
矩陣下標的getter和setter方法都包括了一個斷言語句來檢查下標row和column是否有效。經過indexIsValid方法來判斷row和column是否在矩陣的範圍內:
若是訪問的矩陣越界的時候,斷言就會被觸發:
let someValue = matrix[2, 2]
控制檯打印:assertion failed: Index out of range: file <EXPR>, line 61
十三 繼承
1、定義一個基類
任何一個不繼承於其它類的類被稱做基類
注意:Swift的類不是從一個全局基類繼承而來。在你編寫代碼的時,只要是在類的定義中沒有繼承自父類的類都是基類。
下面的例子定義了一個叫Vehicle的基類。基類包含兩個全部交通工具通用的屬性numberOfWheels和maxPassengers。這兩個屬性被一個叫description的方法使用,經過返回一個String描述來做爲這個交通工具的特徵:
class Vehicle { var numberOfWheels: Int var maxPassengers: Int func description() -> String { return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers" } init() { numberOfWheels = 0 maxPassengers = 1 } }
這個交通工具類Vehicle還定義了一個構造函數來設置它的屬性。
經過構造函數能夠建立一個類型的實例。儘管構造函數不是方法,可是它們在編碼的時候使用了很是類似的語法。構造函數經過確保全部實例的屬性都是有效的來建立一個新的實例。
使用構造函數語法TypeName和空的兩個小括號來完成一個Vehicle實例的建立:
let someVehicle = Vehicle()
Vehicle的構造函數爲屬性設置了一些初始值(numberOfWheels = 0 而後 maxPassengers = 1)。
Vehicle類定義的是一個通用的交通工具特性,它自己沒有太多意義,因此就須要衝定義它的一些屬性或者方法來讓它具備實際的意義。
2、產生子類
產生子類就是根據一個已有的類產生新類的過程。子類繼承了父類的一些能夠修改的特性。還能夠爲子類添加一些新的特性。
爲了代表一個類是繼承自一個父類,須要將父類的名稱寫在子類的後面,而且用冒號分隔
定義一個新的類叫Bicycle,它繼承了Vehicle的特性:
class Bicycle: Vehicle { override init() { super.init() numberOfWheels = 2 } }
不只屬性是繼承於Vehicle的,Bicycle還繼承了父類的方法。若是你建立一個實例,而後調用了已經繼承的description方法,能夠獲得該交通工具的描述而且看到它的屬性已經被修改:
let bicycle = Bicycle() print("Bicycle: \(bicycle.description())")
注意:子類只可以在構造的時候修改變量的屬性,不能修改常量的屬性。
3、重寫方法
子類能夠提供由父類繼承來的實例方法,類方法,實例屬性或者下標的個性化實現。這個特性被稱爲重寫。
重寫一個由繼承而來的方法須要在方法定義前標註override關鍵詞。經過這樣的操做能夠確保你所要修改的這個方法確實是繼承而來的,而不會出現重寫錯誤。錯誤的重寫會形成一些不可預知的錯誤,因此若是若是不標記override關鍵詞的話,就會被在代碼編譯時報錯。
override關鍵詞還可以讓Swift編譯器檢查該類的父類是否有相符的方法,以確保你的重寫是可用的,正確的。
訪問父類方法,屬性和下標
當在重寫子類繼承自父類的方法,屬性或者下標的時候,須要用到一部分父類已有的實現。好比你能夠重定義已知的一個實現或者在繼承的變量中存儲一個修改的值。
適當的時候,能夠經過使用super前綴來訪問父類的方法,屬性或者下標
複寫方法
你能夠在你的子類中實現定製的繼承於父類的實例方法或者類方法。
下面的例子演示的就是一個叫Car的Vehicle子類,重寫了繼承自Vehicle的description方法。
class Car: Vehicle { var speed: Double = 0.0 override init() { super.init() maxPassengers = 5 numberOfWheels = 4 } override func description() -> String { return super.description() + "; " + "traveling at \(speed) mph" } }
Car中定義了一個新的Double類型的存儲屬性speed。這個屬性默認值是0.0,意思是每小時0英里。Car還有一個自定義的構造函數,設置了最大乘客數爲5,輪子數量是4.
Car重寫了繼承的description方法,並在方法名description前標註了override關鍵詞。
在description中並無給出了一個全新的描述實現,仍是經過super.description使用了Vehicle提供的部分描述語句,而後加上了本身定義的一些屬性,如當前速度。
let car = Car() print("Car: \(car.description())")
複寫屬性
你還能夠提供繼承自父類的實例屬性或者類屬性的個性化getter和setter方法,或者是添加屬性觀察者來實現重寫的屬性能夠觀察到繼承屬性的變更。
重寫屬性的Getters和Setters
無論在源類中繼承的這個屬性是存儲屬性仍是計算屬性,你均可以提供一個定製的getter或者setter方法來重寫這個繼承屬性。子類通常不會知道這個繼承的屬性原本是存儲屬性仍是計算屬性,可是它知道這個屬性有特定的名字和類型。在重寫的時候須要指明屬性的類型和名字,好讓編譯器能夠檢查你的重寫是否與父類的屬性相符。
你能夠將一個只讀的屬性經過getter和setter繼承爲可讀寫的,可是反之不可。
注意:若是你爲一個重寫屬性提供了setter方法,那麼也須要提供getter方法。若是你不想在getter中修改繼承的屬性的值,能夠在getter中使用super.someProperty便可,在下面SpeedLimitedCar例子中也是這樣。
下面的例子定義了一個新類SpeedLimitedCar,是Car的一個子類。這個類表示一個顯示在40碼一下的車輛。經過重寫繼承的speed屬性來實現:
class SpeedLimitedCar: Car { override var speed: Double { get { return super.speed } set { super.speed = min(newValue, 40.0) } } }
重寫屬性觀察者
你可使用屬性重寫爲繼承的屬性添加觀察者。這種作法可讓你不管這個屬性以前是如何實現的,在繼承的這個屬性變化的時候都能獲得提醒。
注意:不能爲繼承的常量存儲屬性或者是隻讀計算屬性添加觀察者。這些屬性值是不能被修改的,所以不適合在重寫實現時添加willSet或者didSet方法。
注意:不能同時定義重寫setter和重寫屬性觀察者,若是想要觀察屬性值的變化,而且又爲該屬性給出了定製的setter,那隻須要在setter中直接得到屬性值的變化就好了。
下面的代碼演示的是一個新類AutomaticCar,也是Car的一個子類。這個類代表一個擁有自動變速箱的汽車,能夠根據如今的速度自動選擇檔位,並在description中輸出當前檔位:
class AutomaticCar: Car { var gear = 1 override var speed: Double { didSet { gear = Int(speed / 10.0) + 1 } } override func description() -> String { return super.description() + " in gear \(gear)" } }
這樣就能夠實現,每次你設置speed的值的時候,didSet方法都會被調用,來看檔位是否須要變化。gear是由speed除以10加1計算得來,因此當速度爲35的時候,gear檔位爲4:
let car1:AutomaticCar = AutomaticCar() car1.speed = 35 car1.description()
4、禁止重寫
你能夠經過標記final關鍵詞來禁止重寫一個類的方法,屬性或者下標。在定義的關鍵詞前面標註final屬性便可。
在子類中任未嘗試重寫父類的final方法,屬性或者下標的行爲都會在編譯時報錯。一樣在擴展中爲類添加的方法,屬性或者下標也能夠被標記爲final。
還能夠在類關鍵詞class前使用final標記一整個類爲final(final class)。任何子類嘗試繼承這個父類時都會在編譯時報錯。