1. 枚舉
枚舉為一組相關(guān)的值定義了一個(gè)共同的類(lèi)型赊级,使你可以在你的代碼中以類(lèi)型安全的方式來(lái)使用這些值。
在 Swift 中寿桨,枚舉類(lèi)型是一等(first-class)類(lèi)型此衅。它們采用了很多在傳統(tǒng)上只被類(lèi)(class)所支持的特性强戴,例如計(jì)算屬性(computed properties),用于提供枚舉值的附加信息挡鞍,實(shí)例方法(instance methods)骑歹,用于提供和枚舉值相關(guān)聯(lián)的功能。枚舉也可以定義構(gòu)造函數(shù)(initializers)來(lái)提供一個(gè)初始值墨微;可以在原始實(shí)現(xiàn)的基礎(chǔ)上擴(kuò)展它們的功能道媚;還可以遵循協(xié)議(protocols)來(lái)提供標(biāo)準(zhǔn)的功能。
1.1 枚舉語(yǔ)法
使用enum 關(guān)鍵詞來(lái)創(chuàng)建枚舉并且把它們的整個(gè)定義放在一對(duì)大括號(hào)內(nèi):
enum CompassPoint {
case north
case south
case east
case west
}
注意
與 C 和 Objective-C 不同翘县,Swift 的枚舉成員在被創(chuàng)建時(shí)不會(huì)被賦予一個(gè)默認(rèn)的整型值最域。在上面的CompassPoint 例子中, north 锈麸, south 镀脂, east 和west 不會(huì)被隱式地賦值為0 , 1 忘伞, 2 和3 薄翅。相反,這些枚舉成員本身就是完備的值氓奈,這些值的類(lèi)型是已經(jīng)明確定義好的CompassPoint 類(lèi)型翘魄。
多個(gè)成員值可以出現(xiàn)在同一行上,用逗號(hào)隔開(kāi):
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
1.2 關(guān)聯(lián)值
可以定義 Swift 枚舉來(lái)存儲(chǔ)任意類(lèi)型的關(guān)聯(lián)值舀奶,如果需要的話暑竟,每個(gè)枚舉成員的關(guān)聯(lián)值類(lèi)型可以各不相同。枚舉的這種特性跟其他語(yǔ)言中的可識(shí)別聯(lián)合(discriminated unions)育勺,標(biāo)簽聯(lián)合(tagged unions)但荤,或者變體(variants)相似。
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
1.3 原始值
枚舉成員可以被默認(rèn)值(稱(chēng)為原始值)預(yù)填充怀大,這些原始值的類(lèi)型必須相同纱兑。
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
原始值可以是字符串呀闻,字符化借,或者任意整型值或浮點(diǎn)型值。每個(gè)原始值在枚舉聲明中必須是唯一的捡多。
注意
原始值和關(guān)聯(lián)值是不同的蓖康。原始值是在定義枚舉時(shí)被預(yù)先填充的值,像上述三個(gè) ASCII 碼垒手。對(duì)于一個(gè)特定的枚舉成員蒜焊,它的原始值始終不變。關(guān)聯(lián)值是創(chuàng)建一個(gè)基于枚舉成員的常量或變量時(shí)才設(shè)置的值科贬,枚舉成員的關(guān)聯(lián)值可以變化泳梆。
1.3.1 原始值的隱式賦值
在使用原始值為整數(shù)或者字符串類(lèi)型的枚舉時(shí)鳖悠,不需要顯式地為每一個(gè)枚舉成員設(shè)置原始值,Swift 將會(huì)自動(dòng)為你賦值
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
//Plant.mercury 的顯式原始值為1 优妙, Planet.venus 的隱式原始值為2 乘综,依次類(lèi)推。
當(dāng)使用字符串作為枚舉類(lèi)型的原始值時(shí)套硼,每個(gè)枚舉成員的隱式原始值為該枚舉成員的名稱(chēng)卡辰。
enum CompassPoint: String {
case north, south, east, west
}
//CompassPoint.south 擁有隱式原始值south ,依次類(lèi)推邪意。
使用枚舉成員的rawValue 屬性可以訪問(wèn)該枚舉成員的原始值:
let earthsOrder = Planet.earth.rawValue
// earthsOrder 值為 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection 值為 "west"
1.3.2 使用原始值初始化枚舉實(shí)例
如果在定義枚舉類(lèi)型的時(shí)候使用了原始值九妈,那么將會(huì)自動(dòng)獲得一個(gè)初始化方法,這個(gè)方法接收一個(gè)叫做rawValue的參數(shù)雾鬼,參數(shù)類(lèi)型即為原始值類(lèi)型萌朱,返回值則是枚舉成員或nil 。你可以使用這個(gè)初始化方法來(lái)創(chuàng)建一個(gè)新的枚舉實(shí)例策菜。
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet 類(lèi)型為 Planet? 值為 Planet.uranus
注意
<1>并非所有Int 值都可以找到一個(gè)匹配的行星嚷兔。因此,原始值構(gòu)造器總是返回一個(gè)可選的枚舉成員
<2>原始值構(gòu)造器是一個(gè)可失敗構(gòu)造器做入,因?yàn)椴⒉皇敲恳粋€(gè)原始值都有與之對(duì)應(yīng)的枚舉成員
1.4 遞歸枚舉
遞歸枚舉是一種枚舉類(lèi)型冒晰,它有一個(gè)或多個(gè)枚舉成員使用該枚舉類(lèi)型的實(shí)例作為關(guān)聯(lián)值。使用遞歸枚舉時(shí)竟块,編譯器會(huì)插入一個(gè)間接層壶运。你可以在枚舉成員前加上indirect 來(lái)表示該成員可遞歸。
枚舉類(lèi)型存儲(chǔ)了簡(jiǎn)單的算術(shù)表達(dá)式:
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
你也可以在枚舉類(lèi)型開(kāi)頭加上indirect 關(guān)鍵字來(lái)表明它的所有成員都是可遞歸的:
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
2. 類(lèi)和結(jié)構(gòu)體
Swift 中類(lèi)和結(jié)構(gòu)體有很多共同點(diǎn)浪秘。共同處在于:
? 定義屬性用于存儲(chǔ)值
? 定義方法用于提供功能
? 定義下標(biāo)操作使得可以通過(guò)下標(biāo)語(yǔ)法來(lái)訪問(wèn)實(shí)例所包含的值
? 定義構(gòu)造器用于生成初始化值
? 通過(guò)擴(kuò)展以增加默認(rèn)實(shí)現(xiàn)的功能
? 實(shí)現(xiàn)協(xié)議以提供某種標(biāo)準(zhǔn)功能
與結(jié)構(gòu)體相比蒋情,類(lèi)還有如下的附加功能
? 繼承允許一個(gè)類(lèi)繼承另一個(gè)類(lèi)的特征
? 類(lèi)型轉(zhuǎn)換允許在運(yùn)行時(shí)檢查和解釋一個(gè)類(lèi)實(shí)例的類(lèi)型
? 析構(gòu)器允許一個(gè)類(lèi)實(shí)例釋放任何其所被分配的資源
? 引用計(jì)數(shù)允許對(duì)一個(gè)類(lèi)的多次引用
2.1 定義語(yǔ)法
類(lèi)和結(jié)構(gòu)體有著類(lèi)似的定義方式。我們通過(guò)關(guān)鍵字class 和struct 來(lái)分別表示類(lèi)和結(jié)構(gòu)體耸携,并在一對(duì)大括號(hào)中定義它們的具體內(nèi)容:
class SomeClass {
// 在這里定義類(lèi)
}
struct SomeStructure {
// 在這里定義結(jié)構(gòu)體
}
2.2 類(lèi)和結(jié)構(gòu)體實(shí)例
結(jié)構(gòu)體和類(lèi)都使用構(gòu)造器語(yǔ)法來(lái)生成新的實(shí)例棵癣。構(gòu)造器語(yǔ)法的最簡(jiǎn)單形式是在結(jié)構(gòu)體或者類(lèi)的類(lèi)型名稱(chēng)后跟隨一對(duì)空括號(hào),如Resolution() 或VideoMode() 夺衍。通過(guò)這種方式所創(chuàng)建的類(lèi)或者結(jié)構(gòu)體實(shí)例狈谊,其屬性均會(huì)被初始化為默認(rèn)值
let someResolution = Resolution()
let someVideoMode = VideoMode()
屬性訪問(wèn) - 通過(guò)使用點(diǎn)語(yǔ)法,你可以訪問(wèn)實(shí)例的屬性
print("The width of someResolution is \(someResolution.width)")
// 打印 "The width of someResolution is 0"
也可以訪問(wèn)子屬性
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// 打印 "The width of someVideoMode is 0"
2.2.1 結(jié)構(gòu)體類(lèi)型的成員逐一構(gòu)造器
所有結(jié)構(gòu)體都有一個(gè)自動(dòng)生成的成員逐一構(gòu)造器沟沙,用于初始化新結(jié)構(gòu)體實(shí)例中成員的屬性河劝。新實(shí)例中各個(gè)屬性的初始值可以通過(guò)屬性的名稱(chēng)傳遞到成員逐一構(gòu)造器之中:
let vga = Resolution(width:640, height: 480)
注意:與結(jié)構(gòu)體不同,類(lèi)實(shí)例沒(méi)有默認(rèn)的成員逐一構(gòu)造器
2.3 結(jié)構(gòu)體和枚舉是值類(lèi)型
值類(lèi)型被賦予給一個(gè)變量矛紫、常量或者被傳遞給一個(gè)函數(shù)的時(shí)候赎瞎,其值會(huì)被拷貝。
- 實(shí)際上颊咬,在 Swift 中务甥,所有的基本類(lèi)型:整數(shù)(Integer)牡辽、浮點(diǎn)數(shù)(floating-point)、布爾值(Boolean)敞临、字符串(string)催享、數(shù)組(array)和字典(dictionary),都是值類(lèi)型哟绊,并且在底層都是以結(jié)構(gòu)體的形式所實(shí)現(xiàn)因妙。
- 在 Swift 中,所有的結(jié)構(gòu)體和枚舉類(lèi)型都是值類(lèi)型票髓。這意味著它們的實(shí)例攀涵,以及實(shí)例中所包含的任何值類(lèi)型屬性,在代碼中傳遞的時(shí)候都會(huì)被復(fù)制洽沟。
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
注釋:在以上示例中以故,聲明了一個(gè)名為hd 的常量,其值為一個(gè)初始化為全高清視頻分辨率( 1920 像素寬裆操, 1080 像素高)的Resolution 實(shí)例怒详。然后示例中又聲明了一個(gè)名為cinema 的變量,并將hd 賦值給它踪区。因?yàn)镽esolution 是一個(gè)結(jié)構(gòu)體昆烁,所以cinema的值其實(shí)是hd 的一個(gè)拷貝副本,而不是hd 本身缎岗。盡管hd 和cinema 有著相同的寬(width)和高(height)静尼,但是在幕后它們是兩個(gè)完全不同的實(shí)例。
2.4 類(lèi)是引用類(lèi)型
與值類(lèi)型不同传泊,引用類(lèi)型在被賦予到一個(gè)變量鼠渺、常量或者被傳遞到一個(gè)函數(shù)時(shí),其值不會(huì)被拷貝眷细。因此拦盹,引用的是已存在的實(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"
注釋?zhuān)阂驗(yàn)轭?lèi)是引用類(lèi)型溪椎,所以tenEight 和alsoTenEight 實(shí)際上引用的是相同的VideoMode 實(shí)例普舆。換句話說(shuō),它們是同一個(gè)實(shí)例的兩種叫法池磁。
需要注意的是tenEighty 和alsoTenEighty 被聲明為常量而不是變量奔害。然而你依然可以改變tenEighty.frameRate和alsoTenEighty.frameRate ,因?yàn)閠enEighty 和alsoTenEighty 這兩個(gè)常量的值并未改變地熄。它們并不“存儲(chǔ)”這個(gè)VideoMode 實(shí)例,而僅僅是對(duì)VideoMode 實(shí)例的引用芯杀。所以端考,改變的是被引用的VideoMode 的frameRate 屬性雅潭,而不是引用VideoMode 的常量的值。
2.4.1 恒等運(yùn)算符
因?yàn)轭?lèi)是引用類(lèi)型却特,有可能有多個(gè)常量和變量在幕后同時(shí)引用同一個(gè)類(lèi)實(shí)例扶供。(對(duì)于結(jié)構(gòu)體和枚舉來(lái)說(shuō),這并不成立裂明。因?yàn)樗鼈冏鳛橹殿?lèi)型椿浓,在被賦予到常量、變量或者傳遞到函數(shù)時(shí)闽晦,其值總是會(huì)被拷貝扳碍。)
如果能夠判定兩個(gè)常量或者變量是否引用同一個(gè)類(lèi)實(shí)例將會(huì)很有幫助。為了達(dá)到這個(gè)目的仙蛉,Swift 內(nèi)建了兩個(gè)恒等運(yùn)算符:
? 等價(jià)于( === )
? 不等價(jià)于( !== )
//運(yùn)用這兩個(gè)運(yùn)算符檢測(cè)兩個(gè)常量或者變量是否引用同一個(gè)實(shí)例:
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}
//打印 "tenEighty and alsoTenEighty refer to the same Resolution instance."
注意:
<1>“等價(jià)于”表示兩個(gè)類(lèi)類(lèi)型(class type)的常量或者變量引用同一個(gè)類(lèi)實(shí)例笋敞。
<2>“等于”表示兩個(gè)實(shí)例的值“相等”或“相同”,判定時(shí)要遵照設(shè)計(jì)者定義的評(píng)判標(biāo)準(zhǔn)荠瘪,因此相對(duì)于“相等”來(lái)說(shuō)夯巷,這是一種更加合適的叫法。
2.4.2 指針
如果你有 C哀墓,C++ 或者 Objective-C 語(yǔ)言的經(jīng)驗(yàn)趁餐,那么你也許會(huì)知道這些語(yǔ)言使用指針來(lái)引用內(nèi)存中的地址。一個(gè)引用某個(gè)引用類(lèi)型實(shí)例的 Swift 常量或者變量篮绰,與 C 語(yǔ)言中的指針類(lèi)似澎怒,但是并不直接指向某個(gè)內(nèi)存地址,也不要求你使用星號(hào)( * )來(lái)表明你在創(chuàng)建一個(gè)引用阶牍。Swift 中的這些引用與其它的常量或變量的定義方式相同喷面。
2.5 類(lèi)和結(jié)構(gòu)體的選擇
按照通用的準(zhǔn)則,當(dāng)符合一條或多條以下條件時(shí)走孽,請(qǐng)考慮構(gòu)建結(jié)構(gòu)體:
? 該數(shù)據(jù)結(jié)構(gòu)的主要目的是用來(lái)封裝少量相關(guān)簡(jiǎn)單數(shù)據(jù)值惧辈。
? 有理由預(yù)計(jì)該數(shù)據(jù)結(jié)構(gòu)的實(shí)例在被賦值或傳遞時(shí),封裝的數(shù)據(jù)將會(huì)被拷貝而不是被引用磕瓷。
? 該數(shù)據(jù)結(jié)構(gòu)中儲(chǔ)存的值類(lèi)型屬性盒齿,也應(yīng)該被拷貝,而不是被引用困食。
? 該數(shù)據(jù)結(jié)構(gòu)不需要去繼承另一個(gè)既有類(lèi)型的屬性或者行為边翁。
2.6 字符串、數(shù)組硕盹、和字典類(lèi)型的賦值與復(fù)制行為
Swift 中符匾,許多基本類(lèi)型,諸如String 瘩例, Array 和Dictionary 類(lèi)型均以結(jié)構(gòu)體的形式實(shí)現(xiàn)啊胶。這意味著被賦值給新的常量或變量甸各,或者被傳入函數(shù)或方法中時(shí),它們的值會(huì)被拷貝焰坪。
Objective-C 中NSString 趣倾, NSArray 和NSDictionary 類(lèi)型均以類(lèi)的形式實(shí)現(xiàn),而并非結(jié)構(gòu)體某饰。它們?cè)诒毁x值或者被傳入函數(shù)或方法時(shí)儒恋,不會(huì)發(fā)生值拷貝,而是傳遞現(xiàn)有實(shí)例的引用黔漂。
注意
以上是對(duì)字符串诫尽、數(shù)組、字典的“拷貝”行為的描述瘟仿。在你的代碼中箱锐,拷貝行為看起來(lái)似乎總會(huì)發(fā)生。然而劳较,Swift 在幕后只在絕對(duì)必要時(shí)才執(zhí)行實(shí)際的拷貝驹止。Swift 管理所有的值拷貝以確保性能最優(yōu)化,所以你沒(méi)必要去回避賦值來(lái)保證性能最優(yōu)化观蜗。