通常情況下,我們在使用數(shù)組(Array)或字典(Dictionary)時會使用到下標(biāo)脏答。其實在Swift中问慎,我們還可以給類(class)自定義下標(biāo),下面就讓我們來看看Swift中是如何自定義下標(biāo)的倡勇。
通過Subscript賦值和獲取值
我們先看看下面這個類:
class DailyMeal{
enum MealTime
{
case Breakfast
case Lunch
case Dinner
}
var meals: [MealTime : String] = [:]
}
我們使用該類時可以直接用meals
字典,以枚舉作為key
來查詢嘉涌,像這樣:
var monday = DailyMeal()
monday.meals[.Breakfast] = "Toast"
if let someMeal = monday.meals[.Breakfast]{
println(someMeal)
}
到目前呢妻熊,我們創(chuàng)建了DailyMeal
類的實例變量monday
,并可以使用DailyMeal
類中的meals
字典進行查詢仑最。但是大家有沒有感覺monday.meals[]
這種寫法很累贅呢扔役?至少我看meals
就很不順眼,有沒有更簡單快捷的方法讓我們直接使用monday
變量就能賦值或獲取Breakfast
的值呢警医?別著急亿胸,今天的主角要登場了,讓我們先在DailyMeal
類中添加如下代碼:
subscript(requestedMeal : MealTime) -> String?
{
get {
return meals[requestedMeal]
}
set(newMealName) {
meals[requestedMeal] = newMealName
}
}
上面的代碼就是一個自定義下標(biāo)预皇,看起來是不是有點像計算類屬性的getter
侈玄、setter
方法的寫法呢?但是它們還是有區(qū)別的深啤,首先下標(biāo)使用subscript
關(guān)鍵字拗馒,然后跟一個圓括號,里面是該下標(biāo)的參數(shù)和參數(shù)類型(在實際使用中該參數(shù)就相當(dāng)于數(shù)組的index
和字典的key
一樣)溯街,最后有該下標(biāo)的返回值類型诱桂。
從上面代碼可以看到洋丐,在下標(biāo)的getter
和setter
方法中,其實還是在對meals
數(shù)組進行操作挥等,但是我們通過下標(biāo)就可以將對meals
數(shù)組的操作屏蔽掉∮丫現(xiàn)在來看看我們應(yīng)該怎樣使用:
var monday = DailyMeal()
monday[.Breakfast] = "Toast"
if let someMeal = monday[.Breakfast]{
println(someMeal)
// Toast
}
現(xiàn)在是不是已經(jīng)沒有礙眼的meals
了呢,使用起來更加簡潔肝劲,語義也更加明確了呢迁客,這就是下標(biāo)最簡單的一個用法。
如果上面的代碼中我們不給monday[.Breakfast]
賦值辞槐,直接輸出值會得到什么結(jié)果呢掷漱?細心的同學(xué)可能會注意到,在定義下標(biāo)時它的返回值是Optional
類型的榄檬,所以不賦值直接輸出的結(jié)果是nil
卜范,這樣就顯得太沒禮貌了,所以我們再來改造一下下標(biāo)的代碼:
subscript(requestedMeal : MealTime) -> String
{
get {
if let thisMeal = meals[requestedMeal] {
return thisMeal
} else {
return "Ramen"
}
}
set(newMealName) {
meals[requestedMeal] = newMealName
}
}
我們看到下標(biāo)的返回值從String?
改為了String
鹿榜,那么相應(yīng)的我們要在getter
方法中對meals[requestedMeal]
的值進行判斷海雪,如果沒有賦值的話,我們將返回一個默認值Ramen
(蘭州拉面讓人欲罷不能)〔盏睿現(xiàn)在我們就可以這樣用啦:
var monday = DailyMeal()
monday[.Lunch] = "Pizza"
println(monday[.Lunch])
//Output: "Pizza"
println(monday[.Dinner])
//Output: "Ramen"
現(xiàn)在使用DailyMeal
類是不是感覺到很簡介奥裸,語義很明確也很健壯呢,答案是肯定的沪袭。我們通過下標(biāo)避免向用戶暴露不必要的API湾宙,同時也達到了高維護性的目的。
DailyMeal
類的完整代碼如下:
class DailyMeal{
enum MealTime {
case Breakfast
case Lunch
case Dinner
}
var meals: [MealTime : String] = [:]
subscript(requestedMeal : MealTime) -> String {
get {
if let thisMeal = meals[requestedMeal] {
return thisMeal
} else {
return "Ramen"
}
}
set(newMealName) {
meals[requestedMeal] = newMealName
}
}
}
只讀下標(biāo)
何為只讀下標(biāo)枝恋,顧名思義就是不能通過下標(biāo)賦值创倔,只能通過下標(biāo)查詢嗡害。這種下標(biāo)的應(yīng)用場景一般是實現(xiàn)一些數(shù)據(jù)公式焚碌、數(shù)據(jù)函數(shù),它們一般都是只需要你指定一個數(shù)字霸妹,然后返回該公式對該數(shù)字的計算結(jié)果十电。下面我們用一個階乘的例子來說明只讀下標(biāo):
struct FactorialGenerator{
subscript(n: Int) -> Int {
var result = 1
if n > 0 {
for value in 1...n {
result *= value
}
}
return result
}
}
可能已經(jīng)注意到了,上面的下標(biāo)并沒有getter
和setter
方法叹螟。這是因為鹃骂,如果你想定義一個只讀的下標(biāo),那么可以不實現(xiàn)setter
方法罢绽,并且可以省略getter
方法的get
關(guān)鍵字畏线。Swfit的編譯器會判斷出這是一個只讀的下標(biāo),如果你強行通過下標(biāo)賦值良价,那么編譯器會報錯寝殴。
讓我們來使用以下這個階乘結(jié)構(gòu)體:
let factorial = FactorialGenerator()
println("Five factorial is equal to \(factorial[5]).")
//Output: "Five factorial is equal to 120."
println("Ten Factorial is equal to \(factorial[10]).")
//Output: "Ten Factorial is equal to 3628800."
當(dāng)然上面這個示例只是展示了只讀下標(biāo)的語法和應(yīng)用場景蒿叠,階乘的實現(xiàn)邏輯在這就不累贅了◎汲#總的來說市咽,我們可以通過下標(biāo)簡化暴露給用戶的API,你可以在用戶毫不知情的情況下更改某個API的功能抵蚊。不僅使代碼更易讀施绎,同時也大大提高了代碼的可維護性,是不是很酷呢贞绳!