Swift語法總結(jié)-枚舉,類,結(jié)構(gòu)體

1. 枚舉

枚舉為一組相關(guān)的值定義了一個共同的類型,使你可以在你的代碼中以類型安全的方式來使用這些值。

在 Swift 中,枚舉類型是一等(first-class)類型。它們采用了很多在傳統(tǒng)上只被類(class)所支持的特性,例如計算屬性(computed properties),用于提供枚舉值的附加信息,實例方法(instance methods),用于提供和枚舉值相關(guān)聯(lián)的功能。枚舉也可以定義構(gòu)造函數(shù)(initializers)來提供一個初始值;可以在原始實現(xiàn)的基礎上擴展它們的功能;還可以遵循協(xié)議(protocols)來提供標準的功能。

1.1 枚舉語法

使用enum 關(guān)鍵詞來創(chuàng)建枚舉并且把它們的整個定義放在一對大括號內(nèi):

enum CompassPoint {
    case north
    case south
    case east
    case west
}

注意
與 C 和 Objective-C 不同,Swift 的枚舉成員在被創(chuàng)建時不會被賦予一個默認的整型值。在上面的CompassPoint 例子中, north , south , east 和west 不會被隱式地賦值為0 , 1 , 2 和3 。相反,這些枚舉成員本身就是完備的值,這些值的類型是已經(jīng)明確定義好的CompassPoint 類型。

多個成員值可以出現(xiàn)在同一行上,用逗號隔開:

enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
1.2 關(guān)聯(lián)值

可以定義 Swift 枚舉來存儲任意類型的關(guān)聯(lián)值,如果需要的話,每個枚舉成員的關(guān)聯(lián)值類型可以各不相同。枚舉的這種特性跟其他語言中的可識別聯(lián)合(discriminated unions),標簽聯(lián)合(tagged unions),或者變體(variants)相似。

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}
1.3 原始值

枚舉成員可以被默認值(稱為原始值)預填充,這些原始值的類型必須相同。

enum ASCIIControlCharacter: Character {
    case tab = "\t"
    case lineFeed = "\n"
    case carriageReturn = "\r"
}

原始值可以是字符串,字符,或者任意整型值或浮點型值。每個原始值在枚舉聲明中必須是唯一的。

注意
原始值和關(guān)聯(lián)值是不同的。原始值是在定義枚舉時被預先填充的值,像上述三個 ASCII 碼。對于一個特定的枚舉成員,它的原始值始終不變。關(guān)聯(lián)值是創(chuàng)建一個基于枚舉成員的常量或變量時才設置的值,枚舉成員的關(guān)聯(lián)值可以變化。

1.3.1 原始值的隱式賦值

在使用原始值為整數(shù)或者字符串類型的枚舉時,不需要顯式地為每一個枚舉成員設置原始值,Swift 將會自動為你賦值

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
//Plant.mercury 的顯式原始值為1 , Planet.venus 的隱式原始值為2 ,依次類推。

當使用字符串作為枚舉類型的原始值時,每個枚舉成員的隱式原始值為該枚舉成員的名稱。

enum CompassPoint: String {
    case north, south, east, west
}
//CompassPoint.south 擁有隱式原始值south ,依次類推。

使用枚舉成員的rawValue 屬性可以訪問該枚舉成員的原始值:

let earthsOrder = Planet.earth.rawValue
// earthsOrder 值為 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection 值為 "west"
1.3.2 使用原始值初始化枚舉實例

如果在定義枚舉類型的時候使用了原始值,那么將會自動獲得一個初始化方法,這個方法接收一個叫做rawValue的參數(shù),參數(shù)類型即為原始值類型,返回值則是枚舉成員或nil 。你可以使用這個初始化方法來創(chuàng)建一個新的枚舉實例。

let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet 類型為 Planet? 值為 Planet.uranus

注意
<1>并非所有Int 值都可以找到一個匹配的行星。因此,原始值構(gòu)造器總是返回一個可選的枚舉成員
<2>原始值構(gòu)造器是一個可失敗構(gòu)造器,因為并不是每一個原始值都有與之對應的枚舉成員

1.4 遞歸枚舉

遞歸枚舉是一種枚舉類型,它有一個或多個枚舉成員使用該枚舉類型的實例作為關(guān)聯(lián)值。使用遞歸枚舉時,編譯器會插入一個間接層。你可以在枚舉成員前加上indirect 來表示該成員可遞歸。

枚舉類型存儲了簡單的算術(shù)表達式:

enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}

你也可以在枚舉類型開頭加上indirect 關(guān)鍵字來表明它的所有成員都是可遞歸的:

indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}
2. 類和結(jié)構(gòu)體

Swift 中類和結(jié)構(gòu)體有很多共同點。共同處在于:
? 定義屬性用于存儲值
? 定義方法用于提供功能
? 定義下標操作使得可以通過下標語法來訪問實例所包含的值
? 定義構(gòu)造器用于生成初始化值
? 通過擴展以增加默認實現(xiàn)的功能
? 實現(xiàn)協(xié)議以提供某種標準功能

與結(jié)構(gòu)體相比,類還有如下的附加功能
? 繼承允許一個類繼承另一個類的特征
? 類型轉(zhuǎn)換允許在運行時檢查和解釋一個類實例的類型
? 析構(gòu)器允許一個類實例釋放任何其所被分配的資源
? 引用計數(shù)允許對一個類的多次引用

2.1 定義語法

類和結(jié)構(gòu)體有著類似的定義方式。我們通過關(guān)鍵字class 和struct 來分別表示類和結(jié)構(gòu)體,并在一對大括號中定義它們的具體內(nèi)容:

class SomeClass {
// 在這里定義類
}
struct SomeStructure {
// 在這里定義結(jié)構(gòu)體
}
2.2 類和結(jié)構(gòu)體實例

結(jié)構(gòu)體和類都使用構(gòu)造器語法來生成新的實例。構(gòu)造器語法的最簡單形式是在結(jié)構(gòu)體或者類的類型名稱后跟隨一對空括號,如Resolution() 或VideoMode() 。通過這種方式所創(chuàng)建的類或者結(jié)構(gòu)體實例,其屬性均會被初始化為默認值

let someResolution = Resolution()
let someVideoMode = VideoMode()

屬性訪問 - 通過使用點語法,你可以訪問實例的屬性

print("The width of someResolution is \(someResolution.width)")
// 打印 "The width of someResolution is 0"

也可以訪問子屬性

print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// 打印 "The width of someVideoMode is 0"
2.2.1 結(jié)構(gòu)體類型的成員逐一構(gòu)造器

所有結(jié)構(gòu)體都有一個自動生成的成員逐一構(gòu)造器,用于初始化新結(jié)構(gòu)體實例中成員的屬性。新實例中各個屬性的初始值可以通過屬性的名稱傳遞到成員逐一構(gòu)造器之中:

let vga = Resolution(width:640, height: 480)

注意:與結(jié)構(gòu)體不同,類實例沒有默認的成員逐一構(gòu)造器

2.3 結(jié)構(gòu)體和枚舉是值類型

值類型被賦予給一個變量、常量或者被傳遞給一個函數(shù)的時候,其值會被拷貝。

  1. 實際上,在 Swift 中,所有的基本類型:整數(shù)(Integer)、浮點數(shù)(floating-point)、布爾值(Boolean)、字符串(string)、數(shù)組(array)和字典(dictionary),都是值類型,并且在底層都是以結(jié)構(gòu)體的形式所實現(xiàn)。
  2. 在 Swift 中,所有的結(jié)構(gòu)體和枚舉類型都是值類型。這意味著它們的實例,以及實例中所包含的任何值類型屬性,在代碼中傳遞的時候都會被復制。
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd

注釋:在以上示例中,聲明了一個名為hd 的常量,其值為一個初始化為全高清視頻分辨率( 1920 像素寬, 1080 像素高)的Resolution 實例。然后示例中又聲明了一個名為cinema 的變量,并將hd 賦值給它。因為Resolution 是一個結(jié)構(gòu)體,所以cinema的值其實是hd 的一個拷貝副本,而不是hd 本身。盡管hd 和cinema 有著相同的寬(width)和高(height),但是在幕后它們是兩個完全不同的實例。

2.4 類是引用類型

與值類型不同,引用類型在被賦予到一個變量、常量或者被傳遞到一個函數(shù)時,其值不會被拷貝。因此,引用的是已存在的實例本身而不是其拷貝。

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 打印 "The frameRate property of theEighty is now 30.0"

注釋:因為類是引用類型,所以tenEight 和alsoTenEight 實際上引用的是相同的VideoMode 實例。換句話說,它們是同一個實例的兩種叫法。

需要注意的是tenEighty 和alsoTenEighty 被聲明為常量而不是變量。然而你依然可以改變tenEighty.frameRate和alsoTenEighty.frameRate ,因為tenEighty 和alsoTenEighty 這兩個常量的值并未改變。它們并不“存儲”這個VideoMode 實例,而僅僅是對VideoMode 實例的引用。所以,改變的是被引用的VideoMode 的frameRate 屬性,而不是引用VideoMode 的常量的值。

2.4.1 恒等運算符

因為類是引用類型,有可能有多個常量和變量在幕后同時引用同一個類實例。(對于結(jié)構(gòu)體和枚舉來說,這并不成立。因為它們作為值類型,在被賦予到常量、變量或者傳遞到函數(shù)時,其值總是會被拷貝。)

如果能夠判定兩個常量或者變量是否引用同一個類實例將會很有幫助。為了達到這個目的,Swift 內(nèi)建了兩個恒等運算符:
? 等價于( === )
? 不等價于( !== )

//運用這兩個運算符檢測兩個常量或者變量是否引用同一個實例:
if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}
//打印 "tenEighty and alsoTenEighty refer to the same Resolution instance."

注意:
<1>“等價于”表示兩個類類型(class type)的常量或者變量引用同一個類實例。
<2>“等于”表示兩個實例的值“相等”或“相同”,判定時要遵照設計者定義的評判標準,因此相對于“相等”來說,這是一種更加合適的叫法。

2.4.2 指針

如果你有 C,C++ 或者 Objective-C 語言的經(jīng)驗,那么你也許會知道這些語言使用指針來引用內(nèi)存中的地址。一個引用某個引用類型實例的 Swift 常量或者變量,與 C 語言中的指針類似,但是并不直接指向某個內(nèi)存地址,也不要求你使用星號( * )來表明你在創(chuàng)建一個引用。Swift 中的這些引用與其它的常量或變量的定義方式相同。

2.5 類和結(jié)構(gòu)體的選擇

按照通用的準則,當符合一條或多條以下條件時,請考慮構(gòu)建結(jié)構(gòu)體:
? 該數(shù)據(jù)結(jié)構(gòu)的主要目的是用來封裝少量相關(guān)簡單數(shù)據(jù)值。
? 有理由預計該數(shù)據(jù)結(jié)構(gòu)的實例在被賦值或傳遞時,封裝的數(shù)據(jù)將會被拷貝而不是被引用。
? 該數(shù)據(jù)結(jié)構(gòu)中儲存的值類型屬性,也應該被拷貝,而不是被引用。
? 該數(shù)據(jù)結(jié)構(gòu)不需要去繼承另一個既有類型的屬性或者行為。

2.6 字符串、數(shù)組、和字典類型的賦值與復制行為

Swift 中,許多基本類型,諸如String , Array 和Dictionary 類型均以結(jié)構(gòu)體的形式實現(xiàn)。這意味著被賦值給新的常量或變量,或者被傳入函數(shù)或方法中時,它們的值會被拷貝。

Objective-C 中NSString , NSArray 和NSDictionary 類型均以類的形式實現(xiàn),而并非結(jié)構(gòu)體。它們在被賦值或者被傳入函數(shù)或方法時,不會發(fā)生值拷貝,而是傳遞現(xiàn)有實例的引用。

注意
以上是對字符串、數(shù)組、字典的“拷貝”行為的描述。在你的代碼中,拷貝行為看起來似乎總會發(fā)生。然而,Swift 在幕后只在絕對必要時才執(zhí)行實際的拷貝。Swift 管理所有的值拷貝以確保性能最優(yōu)化,所以你沒必要去回避賦值來保證性能最優(yōu)化。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容