Swift 是一門開發(fā) iOS, macOS, watchOS 和 tvOS 應(yīng)用的新語言。然而跪楞,如果你有 C 或者 Objective-C 開發(fā)經(jīng)驗(yàn)的話,你會(huì)發(fā)現(xiàn) Swift 的很多內(nèi)容都是你熟悉的环疼。
Swift 包含了 C 和 Objective-C 上所有基礎(chǔ)數(shù)據(jù)類型习霹,Int表示整型值;Double和Float表示浮點(diǎn)型值炫隶;Bool是布爾型值淋叶;String是文本型數(shù)據(jù)。 Swift 還提供了三個(gè)基本的集合類型伪阶,Array煞檩,Set和Dictionary,詳見集合類型栅贴。
就像 C 語言一樣斟湃,Swift 使用變量來進(jìn)行存儲(chǔ)并通過變量名來關(guān)聯(lián)值。在 Swift 中檐薯,廣泛的使用著值不可變的變量凝赛,它們就是常量,而且比 C 語言的常量更強(qiáng)大坛缕。在 Swift 中墓猎,如果你要處理的值不需要改變,那使用常量可以讓你的代碼更加安全并且更清晰地表達(dá)你的意圖赚楚。
除了我們熟悉的類型毙沾,Swift 還增加了 Objective-C 中沒有的高階數(shù)據(jù)類型比如元組(Tuple)。元組可以讓你創(chuàng)建或者傳遞一組數(shù)據(jù)宠页,比如作為函數(shù)的返回值時(shí)左胞,你可以用一個(gè)元組可以返回多個(gè)值寇仓。
Swift 還增加了可選(Optional)類型,用于處理值缺失的情況烤宙”榉常可選表示 “那兒有一個(gè)值,并且它等于 x ” 或者 “那兒沒有值” 门烂∪橛洌可選有點(diǎn)像在 Objective-C 中使用nil,但是它可以用在任何類型上屯远,不僅僅是類蔓姚。可選類型比 Objective-C 中的nil指針更加安全也更具表現(xiàn)力慨丐,它是 Swift 許多強(qiáng)大特性的重要組成部分坡脐。
Swift 是一門類型安全的語言,可選類型就是一個(gè)很好的例子房揭。Swift 可以讓你清楚地知道值的類型备闲。如果你的代碼期望得到一個(gè)String,類型安全會(huì)阻止你不小心傳入一個(gè)Int捅暴。同樣的恬砂,如果你的代碼期望得到一個(gè)String,類型安全會(huì)阻止你意外傳入一個(gè)可選的String蓬痒。你可以在開發(fā)階段盡早發(fā)現(xiàn)并修正錯(cuò)誤泻骤。
常量和變量
常量和變量把一個(gè)名字(比如maximumNumberOfLoginAttempts或者welcomeMessage)和一個(gè)指定類型的值(比如數(shù)字10或者字符串"Hello")關(guān)聯(lián)起來。常量的值一旦設(shè)定就不能改變梧奢,而變量的值可以隨意更改狱掂。
聲明常量和變量
常量和變量必須在使用前聲明,用let來聲明常量亲轨,用var來聲明變量趋惨。下面的例子展示了如何用常量和變量來記錄用戶嘗試登錄的次數(shù):
letmaximumNumberOfLoginAttempts =10varcurrentLoginAttempt =0
這兩行代碼可以被理解為:
“聲明一個(gè)名字是maximumNumberOfLoginAttempts的新常量,并給它一個(gè)值10惦蚊。然后器虾,聲明一個(gè)名字是currentLoginAttempt的變量并將它的值初始化為0”姆妫”
在這個(gè)例子中曾撤,允許的最大嘗試登錄次數(shù)被聲明為一個(gè)常量,因?yàn)檫@個(gè)值不會(huì)改變晕粪。當(dāng)前嘗試登錄次數(shù)被聲明為一個(gè)變量,因?yàn)槊看螄L試登錄失敗的時(shí)候都需要增加這個(gè)值渐裸。
你可以在一行中聲明多個(gè)常量或者多個(gè)變量巫湘,用逗號隔開:
varx =0.0, y =0.0, z =0.0
注意:
如果你的代碼中有不需要改變的值装悲,請使用let關(guān)鍵字將它聲明為常量。只將需要改變的值聲明為變量尚氛。
類型標(biāo)注
當(dāng)你聲明常量或者變量的時(shí)候可以加上類型標(biāo)注(type annotation)诀诊,說明常量或者變量中要存儲(chǔ)的值的類型。如果要添加類型標(biāo)注阅嘶,需要在常量或者變量名后面加上一個(gè)冒號和空格属瓣,然后加上類型名稱。
這個(gè)例子給welcomeMessage變量添加了類型標(biāo)注讯柔,表示這個(gè)變量可以存儲(chǔ)String類型的值:
varwelcomeMessage:String
聲明中的冒號代表著“是...類型”抡蛙,所以這行代碼可以被理解為:
“聲明一個(gè)類型為String,名字為welcomeMessage的變量魂迄〈纸兀”
“類型為String”的意思是“可以存儲(chǔ)任意String類型的值〉肪妫”
welcomeMessage變量現(xiàn)在可以被設(shè)置成任意字符串:
welcomeMessage ="Hello"
你可以在一行中定義多個(gè)同樣類型的變量熊昌,用逗號分割,并在最后一個(gè)變量名之后添加類型標(biāo)注:
varred, green, blue:Double
注意:
一般來說你很少需要寫類型標(biāo)注湿酸。如果你在聲明常量或者變量的時(shí)候賦了一個(gè)初始值婿屹,Swift可以推斷出這個(gè)常量或者變量的類型,請參考類型安全和類型推斷推溃。在上面的例子中昂利,沒有給welcomeMessage賦初始值,所以變量welcomeMessage的類型是通過一個(gè)類型標(biāo)注指定的美莫,而不是通過初始值推斷的页眯。
常量和變量的命名
你可以用任何你喜歡的字符作為常量和變量名,包括 Unicode 字符:
letπ =3.14159let你好 ="你好世界"let???? ="dogcow"
常量與變量名不能包含數(shù)學(xué)符號厢呵,箭頭窝撵,保留的(或者非法的)Unicode 碼位,連線與制表符襟铭。也不能以數(shù)字開頭碌奉,但是可以在常量與變量名的其他地方包含數(shù)字。
一旦你將常量或者變量聲明為確定的類型寒砖,你就不能使用相同的名字再次進(jìn)行聲明赐劣,或者改變其存儲(chǔ)的值的類型。同時(shí)哩都,你也不能將常量與變量進(jìn)行互轉(zhuǎn)魁兼。
注意:
如果你需要使用與Swift保留關(guān)鍵字相同的名稱作為常量或者變量名,你可以使用反引號(`)將關(guān)鍵字包圍的方式將其作為名字使用漠嵌。無論如何咐汞,你應(yīng)當(dāng)避免使用關(guān)鍵字作為常量或變量名盖呼,除非你別無選擇。
你可以更改現(xiàn)有的變量值為其他同類型的值化撕,在下面的例子中几晤,friendlyWelcome的值從"Hello!"改為了"Bonjour!":
varfriendlyWelcome ="Hello!"friendlyWelcome ="Bonjour!"http:// friendlyWelcome 現(xiàn)在是 "Bonjour!"
與變量不同,常量的值一旦被確定就不能更改了植阴。嘗試這樣做會(huì)導(dǎo)致編譯時(shí)報(bào)錯(cuò):
letlanguageName ="Swift"languageName ="Swift++"http:// 這會(huì)報(bào)編譯時(shí)錯(cuò)誤 - languageName 不可改變
輸出常量和變量
你可以用print(_:separator:terminator:)函數(shù)來輸出當(dāng)前常量或變量的值:
print(friendlyWelcome)// 輸出 "Bonjour!"
print(_:separator:terminator:)是一個(gè)用來輸出一個(gè)或多個(gè)值到適當(dāng)輸出區(qū)的全局函數(shù)蟹瘾。如果你用 Xcode,print(_:separator:terminator:)將會(huì)輸出內(nèi)容到“console”面板上掠手。separator和terminator參數(shù)具有默認(rèn)值憾朴,因此你調(diào)用這個(gè)函數(shù)的時(shí)候可以忽略它們。默認(rèn)情況下惨撇,該函數(shù)通過添加換行符來結(jié)束當(dāng)前行伊脓。如果不想換行,可以傳遞一個(gè)空字符串給terminator參數(shù)--例如魁衙,print(someValue, terminator:"")报腔。關(guān)于參數(shù)默認(rèn)值的更多信息,請參考默認(rèn)參數(shù)值剖淀。
Swift 用字符串插值(string interpolation)的方式把常量名或者變量名當(dāng)做占位符加入到長字符串中纯蛾,Swift 會(huì)用當(dāng)前常量或變量的值替換這些占位符。將常量或變量名放入圓括號中纵隔,并在開括號前使用反斜杠將其轉(zhuǎn)義:
print("The current value of friendlyWelcome is\(friendlyWelcome)")// 輸出 "The current value of friendlyWelcome is Bonjour!
注意:
字符串插值所有可用的選項(xiàng)翻诉,請參考字符串插值。
注釋
請將你的代碼中的非執(zhí)行文本注釋成提示或者筆記以方便你將來閱讀捌刮。Swift 的編譯器將會(huì)在編譯代碼時(shí)自動(dòng)忽略掉注釋部分碰煌。
Swift 中的注釋與 C 語言的注釋非常相似。單行注釋以雙正斜杠(//)作為起始標(biāo)記:
// 這是一個(gè)注釋
你也可以進(jìn)行多行注釋绅作,其起始標(biāo)記為單個(gè)正斜杠后跟隨一個(gè)星號(/*)芦圾,終止標(biāo)記為一個(gè)星號后跟隨單個(gè)正斜杠(*/):
/* 這是一個(gè),
多行注釋 */
與 C 語言多行注釋不同,Swift 的多行注釋可以嵌套在其它的多行注釋之中俄认。你可以先生成一個(gè)多行注釋塊个少,然后在這個(gè)注釋塊之中再嵌套成第二個(gè)多行注釋。終止注釋時(shí)先插入第二個(gè)注釋塊的終止標(biāo)記眯杏,然后再插入第一個(gè)注釋塊的終止標(biāo)記:
/* 這是第一個(gè)多行注釋的開頭/* 這是第二個(gè)被嵌套的多行注釋 */這是第一個(gè)多行注釋的結(jié)尾 */
通過運(yùn)用嵌套多行注釋夜焦,你可以快速方便的注釋掉一大段代碼,即使這段代碼之中已經(jīng)含有了多行注釋塊岂贩。
分號
與其他大部分編程語言不同茫经,Swift 并不強(qiáng)制要求你在每條語句的結(jié)尾處使用分號(;),當(dāng)然,你也可以按照你自己的習(xí)慣添加分號科平。有一種情況下必須要用分號褥紫,即你打算在同一行內(nèi)寫多條獨(dú)立的語句:
letcat ="??";print(cat)// 輸出 "??"
整數(shù)
整數(shù)就是沒有小數(shù)部分的數(shù)字,比如42和-23瞪慧。整數(shù)可以是有符號(正、負(fù)部念、零)或者無符號(正弃酌、零)。
Swift 提供了8儡炼,16妓湘,32和64位的有符號和無符號整數(shù)類型。這些整數(shù)類型和 C 語言的命名方式很像乌询,比如8位無符號整數(shù)類型是UInt8榜贴,32位有符號整數(shù)類型是Int32。就像 Swift 的其他類型一樣妹田,整數(shù)類型采用大寫命名法唬党。
整數(shù)范圍
你可以訪問不同整數(shù)類型的min和max屬性來獲取對應(yīng)類型的最小值和最大值:
letminValue =UInt8.min// minValue 為 0,是 UInt8 類型letmaxValue =UInt8.max// maxValue 為 255鬼佣,是 UInt8 類型
min和max所傳回值的類型驶拱,正是其所對的整數(shù)類型(如上例UInt8, 所傳回的類型是UInt8),可用在表達(dá)式中相同類型值旁晶衷。
Int
一般來說蓝纲,你不需要專門指定整數(shù)的長度。Swift 提供了一個(gè)特殊的整數(shù)類型Int晌纫,長度與當(dāng)前平臺(tái)的原生字長相同:
在32位平臺(tái)上税迷,Int和Int32長度相同。
在64位平臺(tái)上锹漱,Int和Int64長度相同箭养。
除非你需要特定長度的整數(shù),一般來說使用Int就夠了凌蔬。這可以提高代碼一致性和可復(fù)用性露懒。即使是在32位平臺(tái)上,Int可以存儲(chǔ)的整數(shù)范圍也可以達(dá)到-2,147,483,648~2,147,483,647砂心,大多數(shù)時(shí)候這已經(jīng)足夠大了懈词。
UInt
Swift 也提供了一個(gè)特殊的無符號類型UInt,長度與當(dāng)前平臺(tái)的原生字長相同:
在32位平臺(tái)上辩诞,UInt和UInt32長度相同坎弯。
在64位平臺(tái)上,UInt和UInt64長度相同。
注意:
盡量不要使用UInt抠忘,除非你真的需要存儲(chǔ)一個(gè)和當(dāng)前平臺(tái)原生字長相同的無符號整數(shù)撩炊。除了這種情況,最好使用Int崎脉,即使你要存儲(chǔ)的值已知是非負(fù)的拧咳。統(tǒng)一使用Int可以提高代碼的可復(fù)用性,避免不同類型數(shù)字之間的轉(zhuǎn)換囚灼,并且匹配數(shù)字的類型推斷骆膝,請參考類型安全和類型推斷。
浮點(diǎn)數(shù)
浮點(diǎn)數(shù)是有小數(shù)部分的數(shù)字灶体,比如3.14159阅签,0.1和-273.15。
浮點(diǎn)類型比整數(shù)類型表示的范圍更大蝎抽,可以存儲(chǔ)比Int類型更大或者更小的數(shù)字政钟。Swift 提供了兩種有符號浮點(diǎn)數(shù)類型:
Double表示64位浮點(diǎn)數(shù)。當(dāng)你需要存儲(chǔ)很大或者很高精度的浮點(diǎn)數(shù)時(shí)請使用此類型樟结。
Float表示32位浮點(diǎn)數(shù)养交。精度要求不高的話可以使用此類型。
注意:
Double精確度很高狭吼,至少有15位數(shù)字层坠,而Float只有6位數(shù)字。選擇哪個(gè)類型取決于你的代碼需要處理的值的范圍刁笙,在兩種類型都匹配的情況下破花,將優(yōu)先選擇Double。
類型安全和類型推斷
Swift 是一個(gè)類型安全(type safe)的語言疲吸。類型安全的語言可以讓你清楚地知道代碼要處理的值的類型座每。如果你的代碼需要一個(gè)String,你絕對不可能不小心傳進(jìn)去一個(gè)Int摘悴。
由于 Swift 是類型安全的峭梳,所以它會(huì)在編譯你的代碼時(shí)進(jìn)行類型檢查(type checks),并把不匹配的類型標(biāo)記為錯(cuò)誤蹂喻。這可以讓你在開發(fā)的時(shí)候盡早發(fā)現(xiàn)并修復(fù)錯(cuò)誤葱椭。
當(dāng)你要處理不同類型的值時(shí),類型檢查可以幫你避免錯(cuò)誤口四。然而孵运,這并不是說你每次聲明常量和變量的時(shí)候都需要顯式指定類型。如果你沒有顯式指定類型蔓彩,Swift 會(huì)使用類型推斷(type inference)來選擇合適的類型治笨。有了類型推斷驳概,編譯器可以在編譯代碼的時(shí)候自動(dòng)推斷出表達(dá)式的類型。原理很簡單旷赖,只要檢查你賦的值即可顺又。
因?yàn)橛蓄愋屯茢啵?C 或者 Objective-C 比起來 Swift 很少需要聲明類型等孵。常量和變量雖然需要明確類型稚照,但是大部分工作并不需要你自己來完成。
當(dāng)你聲明常量或者變量并賦初值的時(shí)候類型推斷非常有用俯萌。當(dāng)你在聲明常量或者變量的時(shí)候賦給它們一個(gè)字面量(literal value 或 literal)即可觸發(fā)類型推斷锐锣。(字面量就是會(huì)直接出現(xiàn)在你代碼中的值,比如42和3.14159绳瘟。)
例如,如果你給一個(gè)新常量賦值42并且沒有標(biāo)明類型姿骏,Swift 可以推斷出常量類型是Int糖声,因?yàn)槟憬o它賦的初始值看起來像一個(gè)整數(shù):
letmeaningOfLife =42// meaningOfLife 會(huì)被推測為 Int 類型
同理,如果你沒有給浮點(diǎn)字面量標(biāo)明類型分瘦,Swift 會(huì)推斷你想要的是Double:
letpi =3.14159// pi 會(huì)被推測為 Double 類型
當(dāng)推斷浮點(diǎn)數(shù)的類型時(shí)蘸泻,Swift 總是會(huì)選擇Double而不是Float。
如果表達(dá)式中同時(shí)出現(xiàn)了整數(shù)和浮點(diǎn)數(shù)嘲玫,會(huì)被推斷為Double類型:
letanotherPi =3+0.14159// anotherPi 會(huì)被推測為 Double 類型
原始值3沒有顯式聲明類型悦施,而表達(dá)式中出現(xiàn)了一個(gè)浮點(diǎn)字面量,所以表達(dá)式會(huì)被推斷為Double類型去团。
數(shù)值型字面量
整數(shù)字面量可以被寫作:
一個(gè)十進(jìn)制數(shù)抡诞,沒有前綴
一個(gè)二進(jìn)制數(shù),前綴是0b
一個(gè)八進(jìn)制數(shù)土陪,前綴是0o
一個(gè)十六進(jìn)制數(shù)昼汗,前綴是0x
下面的所有整數(shù)字面量的十進(jìn)制值都是17:
letdecimalInteger =17letbinaryInteger =0b10001// 二進(jìn)制的17letoctalInteger =0o21// 八進(jìn)制的17lethexadecimalInteger =0x11// 十六進(jìn)制的17
浮點(diǎn)字面量可以是十進(jìn)制(沒有前綴)或者是十六進(jìn)制(前綴是0x)。小數(shù)點(diǎn)兩邊必須有至少一個(gè)十進(jìn)制數(shù)字(或者是十六進(jìn)制的數(shù)字)鬼雀。十進(jìn)制浮點(diǎn)數(shù)也可以有一個(gè)可選的指數(shù)(exponent)顷窒,通過大寫或者小寫的e來指定;十六進(jìn)制浮點(diǎn)數(shù)必須有一個(gè)指數(shù)源哩,通過大寫或者小寫的p來指定鞋吉。
如果一個(gè)十進(jìn)制數(shù)的指數(shù)為exp,那這個(gè)數(shù)相當(dāng)于基數(shù)和10^exp的乘積:
1.25e2表示 1.25 × 10^2励烦,等于125.0谓着。
1.25e-2表示 1.25 × 10^-2,等于0.0125崩侠。
如果一個(gè)十六進(jìn)制數(shù)的指數(shù)為exp漆魔,那這個(gè)數(shù)相當(dāng)于基數(shù)和2^exp的乘積:
0xFp2表示 15 × 2^2坷檩,等于60.0。
0xFp-2表示 15 × 2^-2改抡,等于3.75矢炼。
下面的這些浮點(diǎn)字面量都等于十進(jìn)制的12.1875:
letdecimalDouble =12.1875letexponentDouble =1.21875e1lethexadecimalDouble =0xC.3p0
數(shù)值類字面量可以包括額外的格式來增強(qiáng)可讀性。整數(shù)和浮點(diǎn)數(shù)都可以添加額外的零并且包含下劃線阿纤,并不會(huì)影響字面量:
letpaddedDouble =000123.456letoneMillion =1_000_000letjustOverOneMillion =1_000_000.000_000_1
數(shù)值型類型轉(zhuǎn)換
通常來講句灌,即使代碼中的整數(shù)常量和變量已知非負(fù),也請使用Int類型欠拾∫刃浚總是使用默認(rèn)的整數(shù)類型可以保證你的整數(shù)常量和變量可以直接被復(fù)用并且可以匹配整數(shù)類字面量的類型推斷。
只有在必要的時(shí)候才使用其他整數(shù)類型藐窄,比如要處理外部的長度明確的數(shù)據(jù)或者為了優(yōu)化性能资昧、內(nèi)存占用等等。使用顯式指定長度的類型可以及時(shí)發(fā)現(xiàn)值溢出并且可以暗示正在處理特殊數(shù)據(jù)荆忍。
整數(shù)轉(zhuǎn)換
不同整數(shù)類型的變量和常量可以存儲(chǔ)不同范圍的數(shù)字格带。Int8類型的常量或者變量可以存儲(chǔ)的數(shù)字范圍是-128~127,而UInt8類型的常量或者變量能存儲(chǔ)的數(shù)字范圍是0~255刹枉。如果數(shù)字超出了常量或者變量可存儲(chǔ)的范圍叽唱,編譯的時(shí)候會(huì)報(bào)錯(cuò):
letcannotBeNegative:UInt8= -1// UInt8 類型不能存儲(chǔ)負(fù)數(shù),所以會(huì)報(bào)錯(cuò)lettooBig:Int8=Int8.max+1// Int8 類型不能存儲(chǔ)超過最大值的數(shù)微宝,所以會(huì)報(bào)錯(cuò)
由于每種整數(shù)類型都可以存儲(chǔ)不同范圍的值棺亭,所以你必須根據(jù)不同情況選擇性使用數(shù)值型類型轉(zhuǎn)換。這種選擇性使用的方式蟋软,可以預(yù)防隱式轉(zhuǎn)換的錯(cuò)誤并讓你的代碼中的類型轉(zhuǎn)換意圖變得清晰镶摘。
要將一種數(shù)字類型轉(zhuǎn)換成另一種,你要用當(dāng)前值來初始化一個(gè)期望類型的新數(shù)字,這個(gè)數(shù)字的類型就是你的目標(biāo)類型。在下面的例子中粱檀,常量twoThousand是UInt16類型凶伙,然而常量one是UInt8類型。它們不能直接相加,因?yàn)樗鼈冾愋筒煌K砸{(diào)用UInt16(one)來創(chuàng)建一個(gè)新的UInt16數(shù)字并用one的值來初始化,然后使用這個(gè)新數(shù)字來計(jì)算:
lettwoThousand:UInt16=2_000letone:UInt8=1lettwoThousandAndOne = twoThousand +UInt16(one)
現(xiàn)在兩個(gè)數(shù)字的類型都是UInt16俊卤,可以進(jìn)行相加。目標(biāo)常量twoThousandAndOne的類型被推斷為UInt16害幅,因?yàn)樗莾蓚€(gè)UInt16值的和消恍。
SomeType(ofInitialValue)是調(diào)用 Swift 構(gòu)造器并傳入一個(gè)初始值的默認(rèn)方法。在語言內(nèi)部以现,UInt16有一個(gè)構(gòu)造器狠怨,可以接受一個(gè)UInt8類型的值约啊,所以這個(gè)構(gòu)造器可以用現(xiàn)有的UInt8來創(chuàng)建一個(gè)新的UInt16。注意佣赖,你并不能傳入任意類型的值恰矩,只能傳入U(xiǎn)Int16內(nèi)部有對應(yīng)構(gòu)造器的值。不過你可以擴(kuò)展現(xiàn)有的類型來讓它可以接收其他類型的值(包括自定義類型)憎蛤,請參考擴(kuò)展外傅。
整數(shù)和浮點(diǎn)數(shù)轉(zhuǎn)換
整數(shù)和浮點(diǎn)數(shù)的轉(zhuǎn)換必須顯式指定類型:
letthree =3letpointOneFourOneFiveNine =0.14159letpi =Double(three) + pointOneFourOneFiveNine// pi 等于 3.14159,所以被推測為 Double 類型
這個(gè)例子中俩檬,常量three的值被用來創(chuàng)建一個(gè)Double類型的值萎胰,所以加號兩邊的數(shù)類型須相同。如果不進(jìn)行轉(zhuǎn)換棚辽,兩者無法相加技竟。
浮點(diǎn)數(shù)到整數(shù)的反向轉(zhuǎn)換同樣行,整數(shù)類型可以用Double或者Float類型來初始化:
letintegerPi =Int(pi)// integerPi 等于 3屈藐,所以被推測為 Int 類型
當(dāng)用這種方式來初始化一個(gè)新的整數(shù)值時(shí)灵奖,浮點(diǎn)值會(huì)被截?cái)唷R簿褪钦f4.75會(huì)變成4估盘,-3.9會(huì)變成-3。
注意:
結(jié)合數(shù)字類常量和變量不同于結(jié)合數(shù)字類字面量骡尽。字面量3可以直接和字面量0.14159相加遣妥,因?yàn)閿?shù)字字面量本身沒有明確的類型。它們的類型只在編譯器需要求值的時(shí)候被推測攀细。
類型別名
類型別名(type aliases)就是給現(xiàn)有類型定義另一個(gè)名字箫踩。你可以使用typealias關(guān)鍵字來定義類型別名。
當(dāng)你想要給現(xiàn)有類型起一個(gè)更有意義的名字時(shí)谭贪,類型別名非常有用境钟。假設(shè)你正在處理特定長度的外部資源的數(shù)據(jù):
typealiasAudioSample=UInt16
定義了一個(gè)類型別名之后,你可以在任何使用原始名的地方使用別名:
varmaxAmplitudeFound =AudioSample.min// maxAmplitudeFound 現(xiàn)在是 0
本例中俭识,AudioSample被定義為UInt16的一個(gè)別名慨削。因?yàn)樗莿e名,AudioSample.min實(shí)際上是UInt16.min套媚,所以會(huì)給maxAmplitudeFound賦一個(gè)初值0缚态。
布爾值
Swift 有一個(gè)基本的布爾(Boolean)類型,叫做Bool堤瘤。布爾值指邏輯上的值玫芦,因?yàn)樗鼈冎荒苁钦婊蛘呒佟wift 有兩個(gè)布爾常量本辐,true和false:
letorangesAreOrange =trueletturnipsAreDelicious =false
orangesAreOrange和turnipsAreDelicious的類型會(huì)被推斷為Bool桥帆,因?yàn)樗鼈兊某踔凳遣紶栕置媪恳皆觥>拖裰疤岬降腎nt和Double一樣,如果你創(chuàng)建變量的時(shí)候給它們賦值true或者false老虫,那你不需要將常量或者變量聲明為Bool類型叶骨。初始化常量或者變量的時(shí)候如果所賦的值類型已知,就可以觸發(fā)類型推斷张遭,這讓 Swift 代碼更加簡潔并且可讀性更高邓萨。
當(dāng)你編寫條件語句比如if語句的時(shí)候,布爾值非常有用:
ifturnipsAreDelicious {print("Mmm, tasty turnips!")}else{print("Eww, turnips are horrible.")}// 輸出 "Eww, turnips are horrible."
條件語句菊卷,例如if缔恳,請參考控制流。
如果你在需要使用Bool類型的地方使用了非布爾值洁闰,Swift 的類型安全機(jī)制會(huì)報(bào)錯(cuò)歉甚。下面的例子會(huì)報(bào)告一個(gè)編譯時(shí)錯(cuò)誤:
leti =1ifi {// 這個(gè)例子不會(huì)通過編譯,會(huì)報(bào)錯(cuò)}
然而扑眉,下面的例子是合法的:
leti =1ifi ==1{// 這個(gè)例子會(huì)編譯成功}
i == 1的比較結(jié)果是Bool類型纸泄,所以第二個(gè)例子可以通過類型檢查。類似i == 1這樣的比較腰素,請參考基本操作符聘裁。
和 Swift 中的其他類型安全的例子一樣,這個(gè)方法可以避免錯(cuò)誤并保證這塊代碼的意圖總是清晰的弓千。
元組
元組(tuples)把多個(gè)值組合成一個(gè)復(fù)合值衡便。元組內(nèi)的值可以是任意類型,并不要求是相同類型洋访。
下面這個(gè)例子中镣陕,(404, "Not Found")是一個(gè)描述HTTP 狀態(tài)碼(HTTP status code)的元組。HTTP 狀態(tài)碼是當(dāng)你請求網(wǎng)頁的時(shí)候 web 服務(wù)器返回的一個(gè)特殊值姻政。如果你請求的網(wǎng)頁不存在就會(huì)返回一個(gè)404 Not Found狀態(tài)碼呆抑。
lethttp404Error = (404,"Not Found")// http404Error 的類型是 (Int, String),值是 (404, "Not Found")
(404, "Not Found")元組把一個(gè)Int值和一個(gè)String值組合起來表示 HTTP 狀態(tài)碼的兩個(gè)部分:一個(gè)數(shù)字和一個(gè)人類可讀的描述汁展。這個(gè)元組可以被描述為“一個(gè)類型為(Int, String)的元組”鹊碍。
你可以把任意順序的類型組合成一個(gè)元組,這個(gè)元組可以包含所有類型食绿。只要你想妹萨,你可以創(chuàng)建一個(gè)類型為(Int, Int, Int)或者(String, Bool)或者其他任何你想要的組合的元組。
你可以將一個(gè)元組的內(nèi)容分解(decompose)成單獨(dú)的常量和變量炫欺,然后你就可以正常使用它們了:
let(statusCode, statusMessage) = http404Errorprint("The status code is\(statusCode)")// 輸出 "The status code is 404"print("The status message is\(statusMessage)")// 輸出 "The status message is Not Found"
如果你只需要一部分元組值乎完,分解的時(shí)候可以把要忽略的部分用下劃線(_)標(biāo)記:
let(justTheStatusCode,_) = http404Errorprint("The status code is\(justTheStatusCode)")// 輸出 "The status code is 404"
此外,你還可以通過下標(biāo)來訪問元組中的單個(gè)元素品洛,下標(biāo)從零開始:
print("The status code is\(http404Error.0)")// 輸出 "The status code is 404"print("The status message is\(http404Error.1)")// 輸出 "The status message is Not Found"
你可以在定義元組的時(shí)候給單個(gè)元素命名:
lethttp200Status = (statusCode:200, description:"OK")
給元組中的元素命名后树姨,你可以通過名字來獲取這些元素的值:
print("The status code is\(http200Status.statusCode)")// 輸出 "The status code is 200"print("The status message is\(http200Status.description)")// 輸出 "The status message is OK"
作為函數(shù)返回值時(shí)摩桶,元組非常有用。一個(gè)用來獲取網(wǎng)頁的函數(shù)可能會(huì)返回一個(gè)(Int, String)元組來描述是否獲取成功帽揪。和只能返回一個(gè)類型的值比較起來硝清,一個(gè)包含兩個(gè)不同類型值的元組可以讓函數(shù)的返回信息更有用。請參考函數(shù)參數(shù)與返回值转晰。
注意:
元組在臨時(shí)組織值的時(shí)候很有用芦拿,但是并不適合創(chuàng)建復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。如果你的數(shù)據(jù)結(jié)構(gòu)并不是臨時(shí)使用查邢,請使用類或者結(jié)構(gòu)體而不是元組蔗崎。請參考類和結(jié)構(gòu)體。
可選類型
使用可選類型(optionals)來處理值可能缺失的情況扰藕』嚎粒可選類型表示:
有值,等于 x
或者
沒有值
注意:
C 和 Objective-C 中并沒有可選類型這個(gè)概念邓深。最接近的是 Objective-C 中的一個(gè)特性未桥,一個(gè)方法要不返回一個(gè)對象要不返回nil,nil表示“缺少一個(gè)合法的對象”芥备。然而冬耿,這只對對象起作用——對于結(jié)構(gòu)體,基本的 C 類型或者枚舉類型不起作用萌壳。對于這些類型淆党,Objective-C 方法一般會(huì)返回一個(gè)特殊值(比如NSNotFound)來暗示值缺失。這種方法假設(shè)方法的調(diào)用者知道并記得對特殊值進(jìn)行判斷讶凉。然而,Swift 的可選類型可以讓你暗示任意類型的值缺失山孔,并不需要一個(gè)特殊值懂讯。
來看一個(gè)例子。Swift 的Int類型有一種構(gòu)造器台颠,作用是將一個(gè)String值轉(zhuǎn)換成一個(gè)Int值褐望。然而,并不是所有的字符串都可以轉(zhuǎn)換成一個(gè)整數(shù)串前。字符串"123"可以被轉(zhuǎn)換成數(shù)字123瘫里,但是字符串"hello, world"不行。
下面的例子使用這種構(gòu)造器來嘗試將一個(gè)String轉(zhuǎn)換成Int:
letpossibleNumber ="123"letconvertedNumber =Int(possibleNumber)// convertedNumber 被推測為類型 "Int?"荡碾, 或者類型 "optional Int"
因?yàn)樵摌?gòu)造器可能會(huì)失敗谨读,所以它返回一個(gè)可選類型(optional)Int,而不是一個(gè)Int坛吁。一個(gè)可選的Int被寫作Int?而不是Int劳殖。問號暗示包含的值是可選類型铐尚,也就是說可能包含Int值也可能不包含值。(不能包含其他任何值比如Bool值或者String值哆姻。只能是Int或者什么都沒有宣增。)
nil
你可以給可選變量賦值為nil來表示它沒有值:
varserverResponseCode:Int? =404// serverResponseCode 包含一個(gè)可選的 Int 值 404serverResponseCode =nil// serverResponseCode 現(xiàn)在不包含值
注意:
nil不能用于非可選的常量和變量。如果你的代碼中有常量或者變量需要處理值缺失的情況矛缨,請把它們聲明成對應(yīng)的可選類型爹脾。
如果你聲明一個(gè)可選常量或者變量但是沒有賦值,它們會(huì)自動(dòng)被設(shè)置為nil:
varsurveyAnswer:String?// surveyAnswer 被自動(dòng)設(shè)置為 nil
注意:
Swift 的nil和 Objective-C 中的nil并不一樣箕昭。在 Objective-C 中灵妨,nil是一個(gè)指向不存在對象的指針。在 Swift 中盟广,nil不是指針——它是一個(gè)確定的值闷串,用來表示值缺失。任何類型的可選狀態(tài)都可以被設(shè)置為nil筋量,不只是對象類型烹吵。
if 語句以及強(qiáng)制解析
你可以使用if語句和nil比較來判斷一個(gè)可選值是否包含值。你可以使用“相等”(==)或“不等”(!=)來執(zhí)行比較桨武。
如果可選類型有值肋拔,它將不等于nil:
ifconvertedNumber !=nil{print("convertedNumber contains some integer value.")}// 輸出 "convertedNumber contains some integer value."
當(dāng)你確定可選類型確實(shí)包含值之后,你可以在可選的名字后面加一個(gè)感嘆號(!)來獲取值呀酸。這個(gè)驚嘆號表示“我知道這個(gè)可選有值凉蜂,請使用它⌒杂”這被稱為可選值的強(qiáng)制解析(forced unwrapping):
ifconvertedNumber !=nil{print("convertedNumber has an integer value of\(convertedNumber!).")}// 輸出 "convertedNumber has an integer value of 123."
更多關(guān)于if語句的內(nèi)容窿吩,請參考控制流。
注意:
使用!來獲取一個(gè)不存在的可選值會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤错览。使用!來強(qiáng)制解析值之前纫雁,一定要確定可選包含一個(gè)非nil的值。
可選綁定
使用可選綁定(optional binding)來判斷可選類型是否包含值倾哺,如果包含就把值賦給一個(gè)臨時(shí)常量或者變量轧邪。可選綁定可以用在if和while語句中羞海,這條語句不僅可以用來判斷可選類型中是否有值忌愚,同時(shí)可以將可選類型中的值賦給一個(gè)常量或者變量。if和while語句却邓,請參考控制流硕糊。
像下面這樣在if語句中寫一個(gè)可選綁定:
ifletconstantName = someOptional {? ? statements}
你可以像上面這樣使用可選綁定來重寫possibleNumber這個(gè)例子:
ifletactualNumber =Int(possibleNumber) {print("\'\(possibleNumber)\' has an integer value of\(actualNumber)")}else{print("\'\(possibleNumber)\' could not be converted to an integer")}// 輸出 "'123' has an integer value of 123"
這段代碼可以被理解為:
“如果Int(possibleNumber)返回的可選Int包含一個(gè)值,創(chuàng)建一個(gè)叫做actualNumber的新常量并將可選包含的值賦給它“┠唬”
如果轉(zhuǎn)換成功衙耕,actualNumber常量可以在if語句的第一個(gè)分支中使用。它已經(jīng)被可選類型包含的值初始化過勺远,所以不需要再使用!后綴來獲取它的值橙喘。在這個(gè)例子中,actualNumber只被用來輸出轉(zhuǎn)換結(jié)果胶逢。
你可以在可選綁定中使用常量和變量厅瞎。如果你想在if語句的第一個(gè)分支中操作actualNumber的值,你可以改成if var actualNumber初坠,這樣可選類型包含的值就會(huì)被賦給一個(gè)變量而非常量和簸。
你可以包含多個(gè)可選綁定或多個(gè)布爾條件在一個(gè)if語句中,只要使用逗號分開就行碟刺。如果所有可選綁定的值為nil或者所有布爾條件語句都為false锁保,這樣整個(gè)if條件判斷都是為false,這時(shí)你就需要使用嵌套if條件語句來處理半沽,如下所示:
ifletfirstNumber =Int("4"),letsecondNumber =Int("42"), firstNumber < secondNumber && secondNumber <100{print("\(firstNumber)<\(secondNumber)< 100")}// Prints "4 < 42 < 100"ifletfirstNumber =Int("4") {ifletsecondNumber =Int("42") {iffirstNumber < secondNumber && secondNumber <100{print("\(firstNumber)<\(secondNumber)< 100")? ? ? ? }? ? }}// Prints "4 < 42 < 100"
注意: 在if條件語句中使用常量和變量來創(chuàng)建一個(gè)可選綁定爽柒,僅在if語句的句中(body)中才能獲取到值。相反者填,在guard語句中使用常量和變量來創(chuàng)建一個(gè)可選綁定浩村,僅在guard語句外且在語句后才能獲取到值,請參考控制流占哟。
隱式解析可選類型
如上所述心墅,可選類型暗示了常量或者變量可以“沒有值”≌ズ酰可選可以通過if語句來判斷是否有值怎燥,如果有值的話可以通過可選綁定來解析值。
有時(shí)候在程序架構(gòu)中蜜暑,第一次被賦值之后铐姚,可以確定一個(gè)可選類型總會(huì)有值。在這種情況下史煎,每次都要判斷和解析可選值是非常低效的,因?yàn)榭梢源_定它總會(huì)有值驳糯。
這種類型的可選狀態(tài)被定義為隱式解析可選類型(implicitly unwrapped optionals)篇梭。把想要用作可選的類型的后面的問號(String?)改成感嘆號(String!)來聲明一個(gè)隱式解析可選類型。
當(dāng)可選類型被第一次賦值之后就可以確定之后一直有值的時(shí)候酝枢,隱式解析可選類型非常有用恬偷。隱式解析可選類型主要被用在 Swift 中類的構(gòu)造過程中,請參考無主引用以及隱式解析可選屬性帘睦。
一個(gè)隱式解析可選類型其實(shí)就是一個(gè)普通的可選類型袍患,但是可以被當(dāng)做非可選類型來使用坦康,并不需要每次都使用解析來獲取可選值。下面的例子展示了可選類型String和隱式解析可選類型String之間的區(qū)別:
letpossibleString:String? ="An optional string."letforcedString:String= possibleString!// 需要驚嘆號來獲取值letassumedString:String! ="An implicitly unwrapped optional string."letimplicitString:String= assumedString// 不需要感嘆號
你可以把隱式解析可選類型當(dāng)做一個(gè)可以自動(dòng)解析的可選類型诡延。你要做的只是聲明的時(shí)候把感嘆號放到類型的結(jié)尾滞欠,而不是每次取值的可選名字的結(jié)尾。
注意:
如果你在隱式解析可選類型沒有值的時(shí)候嘗試取值肆良,會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤筛璧。和你在沒有值的普通可選類型后面加一個(gè)驚嘆號一樣。
你仍然可以把隱式解析可選類型當(dāng)做普通可選類型來判斷它是否包含值:
ifassumedString !=nil{print(assumedString)}// 輸出 "An implicitly unwrapped optional string."
你也可以在可選綁定中使用隱式解析可選類型來檢查并解析它的值:
ifletdefiniteString = assumedString {print(definiteString)}// 輸出 "An implicitly unwrapped optional string."
注意:
如果一個(gè)變量之后可能變成nil的話請不要使用隱式解析可選類型惹恃。如果你需要在變量的生命周期中判斷是否是nil的話夭谤,請使用普通可選類型。
錯(cuò)誤處理
你可以使用錯(cuò)誤處理(error handling)來應(yīng)對程序執(zhí)行中可能會(huì)遇到的錯(cuò)誤條件巫糙。
相對于可選中運(yùn)用值的存在與缺失來表達(dá)函數(shù)的成功與失敗朗儒,錯(cuò)誤處理可以推斷失敗的原因,并傳播至程序的其他部分参淹。
當(dāng)一個(gè)函數(shù)遇到錯(cuò)誤條件醉锄,它能報(bào)錯(cuò)。調(diào)用函數(shù)的地方能拋出錯(cuò)誤消息并合理處理承二。
funccanThrowAnError()throws {// 這個(gè)函數(shù)有可能拋出錯(cuò)誤}
一個(gè)函數(shù)可以通過在聲明中添加throws關(guān)鍵詞來拋出錯(cuò)誤消息榆鼠。當(dāng)你的函數(shù)能拋出錯(cuò)誤消息時(shí), 你應(yīng)該在表達(dá)式中前置try關(guān)鍵詞。
do{? ? try canThrowAnError()// 沒有錯(cuò)誤消息拋出} catch {// 有一個(gè)錯(cuò)誤消息拋出}
一個(gè)do語句創(chuàng)建了一個(gè)新的包含作用域,使得錯(cuò)誤能被傳播到一個(gè)或多個(gè)catch從句亥鸠。
這里有一個(gè)錯(cuò)誤處理如何用來應(yīng)對不同錯(cuò)誤條件的例子妆够。
funcmakeASandwich()throws {// ...}do{? ? try makeASandwich()? ? eatASandwich()} catchSandwichError.outOfCleanDishes {? ? washDishes()} catchSandwichError.missingIngredients(letingredients) {? ? buyGroceries(ingredients)}
在此例中,makeASandwich()(做一個(gè)三明治)函數(shù)會(huì)拋出一個(gè)錯(cuò)誤消息如果沒有干凈的盤子或者某個(gè)原料缺失负蚊。因?yàn)閙akeASandwich()拋出錯(cuò)誤神妹,函數(shù)調(diào)用被包裹在try表達(dá)式中。將函數(shù)包裹在一個(gè)do語句中家妆,任何被拋出的錯(cuò)誤會(huì)被傳播到提供的catch從句中鸵荠。
如果沒有錯(cuò)誤被拋出,eatASandwich()函數(shù)會(huì)被調(diào)用伤极。如果一個(gè)匹配SandwichError.outOfCleanDishes的錯(cuò)誤被拋出蛹找,washDishes()函數(shù)會(huì)被調(diào)用。如果一個(gè)匹配SandwichError.missingIngredients的錯(cuò)誤被拋出哨坪,buyGroceries(_:)函數(shù)會(huì)被調(diào)用庸疾,并且使用catch所捕捉到的關(guān)聯(lián)值[String]作為參數(shù)。
拋出当编,捕捉届慈,以及傳播錯(cuò)誤會(huì)在錯(cuò)誤處理章節(jié)詳細(xì)說明。
斷言
可選類型可以讓你判斷值是否存在,你可以在代碼中優(yōu)雅地處理值缺失的情況金顿。然而臊泌,在某些情況下,如果值缺失或者值并不滿足特定的條件揍拆,你的代碼可能沒辦法繼續(xù)執(zhí)行渠概。這時(shí),你可以在你的代碼中觸發(fā)一個(gè)斷言(assertion)來結(jié)束代碼運(yùn)行并通過調(diào)試來找到值缺失的原因礁凡。
使用斷言進(jìn)行調(diào)試
斷言會(huì)在運(yùn)行時(shí)判斷一個(gè)邏輯條件是否為true高氮。從字面意思來說,斷言“斷言”一個(gè)條件是否為真顷牌。你可以使用斷言來保證在運(yùn)行其他代碼之前剪芍,某些重要的條件已經(jīng)被滿足。如果條件判斷為true窟蓝,代碼運(yùn)行會(huì)繼續(xù)進(jìn)行罪裹;如果條件判斷為false,代碼執(zhí)行結(jié)束运挫,你的應(yīng)用被終止状共。
如果你的代碼在調(diào)試環(huán)境下觸發(fā)了一個(gè)斷言,比如你在 Xcode 中構(gòu)建并運(yùn)行一個(gè)應(yīng)用谁帕,你可以清楚地看到不合法的狀態(tài)發(fā)生在哪里并檢查斷言被觸發(fā)時(shí)你的應(yīng)用的狀態(tài)峡继。此外,斷言允許你附加一條調(diào)試信息匈挖。
你可以使用全局assert(_:_:file:line:)函數(shù)來寫一個(gè)斷言碾牌。向這個(gè)函數(shù)傳入一個(gè)結(jié)果為true或者false的表達(dá)式以及一條信息,當(dāng)表達(dá)式的結(jié)果為false的時(shí)候這條信息會(huì)被顯示:
letage = -3assert(age >=0,"A person's age cannot be less than zero")// 因?yàn)?age < 0儡循,所以斷言會(huì)觸發(fā)
在這個(gè)例子中舶吗,只有age >= 0為true的時(shí)候,即age的值非負(fù)的時(shí)候择膝,代碼才會(huì)繼續(xù)執(zhí)行誓琼。如果age的值是負(fù)數(shù),就像代碼中那樣肴捉,age >= 0為false腹侣,斷言被觸發(fā),終止應(yīng)用齿穗。
如果不需要斷言信息傲隶,可以省略,就像這樣:
assert(age >=0)
注意:
當(dāng)代碼使用優(yōu)化編譯的時(shí)候缤灵,斷言將會(huì)被禁用伦籍,例如在 Xcode 中,使用默認(rèn)的 target Release 配置選項(xiàng)來 build 時(shí)腮出,斷言會(huì)被禁用帖鸦。
何時(shí)使用斷言
當(dāng)條件可能為假時(shí)使用斷言,但是最終一定要保證條件為真胚嘲,這樣你的代碼才能繼續(xù)運(yùn)行作儿。斷言的適用情景:
整數(shù)類型的下標(biāo)索引被傳入一個(gè)自定義下標(biāo)實(shí)現(xiàn),但是下標(biāo)索引值可能太小或者太大馋劈。
需要給函數(shù)傳入一個(gè)值攻锰,但是非法的值可能導(dǎo)致函數(shù)不能正常執(zhí)行。
一個(gè)可選值現(xiàn)在是nil妓雾,但是后面的代碼運(yùn)行需要一個(gè)非nil值娶吞。