引言
繼續(xù)學(xué)習(xí)Swift文檔,從上一章節(jié):開篇 ,我們了解Swift基本的知識(shí)點(diǎn)勋又,現(xiàn)在我們還是從詳細(xì)的基礎(chǔ)知識(shí)開始楔壤,不要認(rèn)為基礎(chǔ)知識(shí)不重要惯驼,這是掌握一門開發(fā)語言的基本。由于篇幅較長抖部,這里分篇來記錄慎颗,接下來俯萎,開始吧讯屈!
如果你已經(jīng)掌握了Swift的基礎(chǔ),那么請參閱下一章節(jié):基本操作
基礎(chǔ)
Swift是一門新的針對(duì)iOS躁愿、macOS、watchOS和tvOS開發(fā)的編程語言逸雹。盡管如此梆砸,根據(jù)您使用C和Objective-C進(jìn)行開發(fā)的經(jīng)驗(yàn)帖世,您將熟悉Swift的許多部分日矫。
Swift提供了自己版本的所有基本C和Objective-C類型哪轿,包括Int對(duì)應(yīng)整型窃诉,Double和Float對(duì)應(yīng)浮點(diǎn)類型褐奴,Bool對(duì)應(yīng)布爾類型敦冬,和String對(duì)應(yīng)文本數(shù)據(jù)脖旱。Swift還提供了三種主要收藏類型的強(qiáng)大版本萌庆,Array践险,Set和Dictionary巍虫,定義在 Collection Types.
像C一樣占遥,Swift使用變量來存儲(chǔ)和通過一個(gè)標(biāo)識(shí)名稱引用值瓦胎。Swift還大量使用了不能更改值的變量搔啊。這些被稱為常量坯癣,比c中的常量功能強(qiáng)大得多示罗。當(dāng)你處理不需要更改的值時(shí)蚜点,在Swift中使用常量使代碼更安全奶镶、更清晰厂镇。
除了熟悉的類型捺信,Swift還引入了Objective-C中沒有的高級(jí)類型迄靠,比如元組掌挚。元組使您能夠創(chuàng)建和傳遞值分組吠式√卣迹可以使用元組將函數(shù)中的多個(gè)值作為一個(gè)復(fù)合值返回摩钙。
Swift還引入了可選類型,用于處理缺少值的情況长踊∩肀祝可選值要么說“有一個(gè)值阱佛,它等于x”凑术,要么說“根本沒有值”淮逊。使用可選值類似于在Objective-C中對(duì)指針使用nil泄鹏,但它們適用于任何類型舶治,而不僅僅是類歼疮。option不僅比Objective-C中的nil指針更安全韩脏、更有表現(xiàn)力赡矢,而且是Swift許多最強(qiáng)大功能的核心吹散。
Swift是一種類型安全的語言空民,這意味著該語言可以幫助您清楚地了解代碼可以使用的值的類型。如果您的部分代碼需要String浊猾,類型安全防止您錯(cuò)誤地將其傳遞為Int葫慎。同樣偷办,類型安全可以防止您意外地將可選String傳遞給需要非可選String的代碼段爽篷。類型安全可以幫助您盡早捕獲和修復(fù)開發(fā)過程中的錯(cuò)誤铡溪。
1 常量和變量
常量和變量將名稱(如maximumnumberoflogintries或welcomeMessage)與特定類型的值(如數(shù)字10或字符串“Hello”)關(guān)聯(lián)起來棕硫。常量的值一旦設(shè)置就不能更改哈扮,而變量可以在將來設(shè)置為不同的值。
1.1 定義常量和變量
常量額變量在使用前必須聲明靶庙×模可以用let關(guān)鍵字聲明常量掏击,用var關(guān)鍵字聲明變量。下面是一個(gè)如何使用常量和變量來跟蹤用戶嘗試登錄的次數(shù)的例子:
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
這段代碼意思是:
“定義一個(gè)maximumNumberOfLoginAttempts常量钠惩,并賦值為10。定義一個(gè)currentLoginAttempt變量,并賦值為0.”
在這個(gè)例子中鲤遥,聲明了一個(gè)最大允許登錄次數(shù)的常量盖奈,因?yàn)檫@個(gè)最大值沒有被修改钢坦。當(dāng)前登錄嘗試計(jì)數(shù)器被聲明為一個(gè)變量厨诸,因?yàn)樵诿看蔚卿泧L試失敗后微酬,該值必須遞增。
你可以在一行里聲明多個(gè)常量和多個(gè)變量垦江,用逗號(hào)分隔:
var x = 0.0, y = 0.0, z = 0.0
注意
如果代碼中存儲(chǔ)的值不會(huì)改變疫粥,請始終使用let關(guān)鍵字將其聲明為常量梗逮。變量只用于存儲(chǔ)需要更改的值。
1.2 類型注解
當(dāng)你聲明常量和變量時(shí)底哗,可以添加類型,這樣就可以知道需要存儲(chǔ)的值的類型前标。通過在常量或變量名后面加上冒號(hào)、空格和要使用的類型名來編寫類型注釋距潘。
這個(gè)示例為一個(gè)名為welcomeMessage的變量提供了一個(gè)類型注釋炼列,以表明該變量可以存儲(chǔ)字符串值:
var welcomeMessage: String
聲明中的冒號(hào)表示“…of type…”,因此上面的代碼可以理解為:
“定義一個(gè)名稱叫welcomeMessage的字符串變量音比〖蠹猓”
短語“of type String”意味著“可以存儲(chǔ)任何字符串值”。把它看作是可以存儲(chǔ)的“事物的類型”(或“事物的種類”)的意思稽犁。
welcomeMessage變量可以設(shè)置為任何字符串值:
welcomeMessage = "Hello"
你可以在一行中定義多個(gè)相同類型的相關(guān)變量焰望,用逗號(hào)分隔,最后的變量名后面有一個(gè)類型注釋:
var red, green, blue: Double
實(shí)際上缭付,很少需要編寫類型注釋柿估。如果在定義常量或變量時(shí)提供初始值,Swift幾乎總是可以推斷該常量或變量使用的類型陷猫,如 類型安全和類型推斷
所述秫舌。在上面的welcomeMessage示例中,沒有提供初始值绣檬,因此welcomeMessage變量的類型是通過類型注釋指定的足陨,而不是從初始值推斷出來的。
1.3 命名常量和變量
常量和變量的名稱支持幾乎所有字符娇未,包括Unicode字符:
let π = 3.14159
let 你好 = "你好世界"
let ???? = "dogcow"
常量個(gè)變量的名稱不能包含空格墨缘,數(shù)學(xué)符號(hào),箭頭零抬,私有Unicode標(biāo)量值镊讼,或-和box-drawing字符。也不能以數(shù)字開頭平夜,盡管數(shù)字可以包含在名稱的其他地方蝶棋。
一旦聲明了一個(gè)某些類型的常量和變量,就不能聲明和它相同名稱的忽妒,或者改變它存儲(chǔ)的類型玩裙。盡管可以將一個(gè)常量修改為變量或者一個(gè)變量修改為常量。
注意
如果您需要為一個(gè)常量或變量提供與保留的Swift關(guān)鍵字相同的名稱段直,在使用該關(guān)鍵字作為名稱時(shí)使用反引號(hào)(`)吃溅。但是,除非別無選擇鸯檬,否則要避免使用關(guān)鍵字作為名稱决侈。
可以將現(xiàn)有變量的值更改為另一個(gè)兼容類型的值,例如:
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"
和變量不同的是喧务,常量一旦設(shè)置了值颜及,就不能再修改,如果強(qiáng)行修改會(huì)報(bào)錯(cuò):
let languageName = "Swift"
languageName = "Swift++"
// This is a compile-time error: languageName cannot be changed.
1.4 打印常量和變量
可以通過print(_:separator:terminator:)函數(shù)打印常量或變量:
print(friendlyWelcome)
// Prints "Bonjour!"
print(_:separator:terminator:)函數(shù)是一個(gè)全局函數(shù)蹂楣,它將在適當(dāng)?shù)妮敵鲋写蛴∫粋€(gè)或多個(gè)值。在Xcode中讯蒲,例如痊土,print(_:separator:terminator:)函數(shù)會(huì)在Xcode的控制臺(tái)上輸出打印的值。 separator和 terminator有默認(rèn)值墨林,因此當(dāng)調(diào)用這個(gè)函數(shù)時(shí)可以忽略它們赁酝。默認(rèn)情況下犯祠,函數(shù)通過添加換行符來終止它打印的行。若要打印值后不帶換行符酌呆,請傳遞一個(gè)空字符串作為終止符--例如: print(someValue, terminator: "")衡载。有關(guān)具有默認(rèn)值的參數(shù)的信息,參閱 Default Parameter Values隙袁。
Swift使用字符串插值將常量或變量的名稱作為占位符包含在較長的字符串中痰娱,并提示Swift將其替換為該常數(shù)或變量的當(dāng)前值。用括號(hào)括起名字菩收,在括號(hào)前面用反斜杠轉(zhuǎn)義:
print("The current value of friendlyWelcome is \(friendlyWelcome)")
// Prints "The current value of friendlyWelcome is Bonjour!"
注意
字符串插值可以使用的所有選項(xiàng)在String Interpolation都有描述梨睁。
2 注釋
使用注釋將不可執(zhí)行文本包含在代碼中,作為對(duì)自己的提示或提醒娜饵。代碼編譯時(shí)坡贺,Swift編譯器會(huì)忽略注釋。
Swift中的注釋和c的很相似箱舞。單行注釋用雙斜杠(//):
// This is a comment.
多行只是可以用/* ... */:
/* This is also a comment
but is written over multiple lines. */
與C中的多行注釋不同遍坟,Swift中的多行注釋可以嵌套在其他多行注釋中。您可以通過啟動(dòng)一個(gè)多行注釋塊晴股,然后在第一個(gè)注釋塊中啟動(dòng)第二個(gè)多行注釋來編寫嵌套注釋愿伴。然后關(guān)閉第二個(gè)區(qū)塊,然后關(guān)閉第一個(gè)區(qū)塊:
/* This is the start of the first multiline comment.
/* This is the second, nested multiline comment. */
This is the end of the first multiline comment. */
嵌套多行注釋使您能夠快速而輕松地注釋掉大塊代碼队魏,即使代碼已經(jīng)包含多行注釋公般。
3 分號(hào)
和許多其他語言不同,Swift不需要在語句末尾寫分號(hào)胡桨,盡管你希望這樣做官帘。然而,當(dāng)將多個(gè)語句寫在同一行時(shí)昧谊,需要加分號(hào)分隔:
let cat = "??"; print(cat)
// Prints "??"
4 整型
整數(shù)是沒有分?jǐn)?shù)成分的整數(shù)刽虹,例如42和-23。整數(shù)可以是有符號(hào)的(正呢诬、零或負(fù))或無符號(hào)的(正或零)涌哲。
Swift提供8位、16位尚镰、32位和64位格式的有符號(hào)整數(shù)和無符號(hào)整數(shù)阀圾。這些整數(shù)遵循類似于C的命名約定,即8位無符號(hào)整數(shù)的類型為UInt8, 32位有符號(hào)整數(shù)的類型為Int32狗唉。與Swift中的所有類型一樣初烘,這些整數(shù)類型的名稱都是大寫的。
4.1 整數(shù)范圍
您可以訪問每個(gè)整數(shù)類型的最小值和最大值及其min和max屬性:
let minValue = UInt8.min // minValue is equal to 0, and is of type UInt8
let maxValue = UInt8.max // maxValue is equal to 255, and is of type UInt8
這些屬性的值具有適當(dāng)大小的數(shù)字類型(如上面示例中的UInt8),因此可以在表達(dá)式中與其他相同類型的值一起使用肾筐。
4.2 Int
在大多數(shù)情況下哆料,您不需要在代碼中選擇特定大小的整數(shù)。Swift提供了一個(gè)額外的整數(shù)類型Int吗铐,它與當(dāng)前平臺(tái)的本地字大小相同:
- 在32位平臺(tái)上东亦,Int大小和Int32相同。
- 在64位平臺(tái)上唬渗,Int大小和Int64相同典阵。
除非你需要處理一個(gè)特定大小的整數(shù),否則整數(shù)都要用Int定義谣妻。這有助于代碼的一致性和互操作性萄喳。即使在32位平臺(tái)上,Int可以存儲(chǔ)的值在-2,147,483,648和2,147,483,647之間蹋半,這個(gè)范圍可以用在大多數(shù)整數(shù)上了他巨。
4.3 UInt
Swift也提供了無符號(hào)的整型,UInt减江,它與當(dāng)前平臺(tái)的本地字大小相同:
- 在32位平臺(tái)上染突,UInt大小和UInt32相同。
- 在64位平臺(tái)上辈灼,UInt大小和UInt64相同份企。
注意
僅當(dāng)您特別需要與平臺(tái)的本地字大小相同的無符號(hào)整數(shù)類型時(shí),才使用UInt巡莹。如果不是這樣司志,則首選Int,即使已知要存儲(chǔ)的值是非負(fù)的降宅。整數(shù)值一致地使用Int有助于代碼的互操作性骂远,避免不同數(shù)字類型之間的轉(zhuǎn)換,并匹配整型類型推斷腰根,如類型安全和類型推斷中所述激才。
5 Floating-浮點(diǎn)數(shù)
浮點(diǎn)數(shù)是由小數(shù)部分組成的數(shù),如3.14159额嘿、0.1和-273.15瘸恼。
與整型相比,浮點(diǎn)型可以代表更大范圍的值册养,并且可以存儲(chǔ)比整型更大或更小的數(shù)字东帅。Swift提供了兩種帶符號(hào)的浮點(diǎn)數(shù)類型:
- Double表示一個(gè)64位浮點(diǎn)數(shù)。
- Float表示一個(gè)32位浮點(diǎn)數(shù)球拦。
注意
Double的精度至少為15位十進(jìn)制數(shù)字靠闭,而Float的精度只有6位十進(jìn)制數(shù)字邓夕。要使用的適當(dāng)浮點(diǎn)類型取決于代碼中需要使用的值的性質(zhì)和范圍。如果兩種類型都合適阎毅,則首選Double。
6 類型安全和類型推斷
Swift是一種類型安全的語言点弯,類型安全語言鼓勵(lì)您明確代碼可以使用的值的類型扇调。如果代碼的一部分需要字符串,則不能錯(cuò)誤地將其傳遞為Int抢肛。
因?yàn)镾wift是類型安全的狼钮,它在編譯代碼時(shí)執(zhí)行類型檢查,并將任何不匹配的類型標(biāo)記為錯(cuò)誤捡絮。這使您能夠在開發(fā)過程中盡早捕獲和修復(fù)錯(cuò)誤熬芜。
當(dāng)您使用不同類型的值時(shí),類型檢查可以幫助您避免錯(cuò)誤福稳。但是涎拉,這并不意味著必須指定聲明的每個(gè)常量和變量的類型。如果沒有指定所需值的類型的圆,Swift會(huì)使用類型推斷來計(jì)算出適當(dāng)?shù)念愋凸呐 n愋屯茢嗍咕幾g器在編譯代碼時(shí)能夠通過檢查提供的值自動(dòng)推斷特定表達(dá)式的類型。
由于有了類型推斷越妈,Swift需要的類型聲明要比C或Objective-C等語言少得多季俩。常量和變量仍然是顯式類型的,但是指定它們的類型的大部分工作已經(jīng)為您完成了梅掠。
在聲明具有初始值的常量或變量時(shí)酌住,類型推斷特別有用。這通常是通過在聲明常量或變量時(shí)為其分配一個(gè)文字值(或文字)來實(shí)現(xiàn)的阎抒。(文字值是直接出現(xiàn)在源代碼中的值酪我,例如下面示例中的42和3.14159。)
例如挠蛉,如果你給一個(gè)新常量賦值為42而沒有說明它是什么類型祭示,Swift推斷你希望這個(gè)常量是一個(gè)整型數(shù),因?yàn)槟阌靡粋€(gè)看起來像整數(shù)的數(shù)字初始化了它:
let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int
同樣地谴古,如果你沒有指定一個(gè)浮點(diǎn)型文字的類型质涛,Swift推斷你想要?jiǎng)?chuàng)建一個(gè)Double:
let pi = 3.14159
// pi is inferred to be of type Double
在推斷浮點(diǎn)數(shù)類型時(shí),Swift總是選擇Double(而不是Float)掰担。
如果你在一個(gè)表達(dá)式中結(jié)合了整型和浮點(diǎn)型文字汇陆,Double類型會(huì)從上下文推斷出來:
let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double
字面值3本身沒有顯式的類型,因此可以從作為加法的一部分的浮點(diǎn)字面值中推斷出適當(dāng)?shù)妮敵鲱愋虳ouble带饱。
7 數(shù)字字面值
整型文字可以寫成:
- 無前綴的十進(jìn)制數(shù)
- 一種二進(jìn)制數(shù)毡代,前綴為0b
- 帶有0o前綴的八進(jìn)制數(shù)
- 帶有0x前綴的十六進(jìn)制數(shù)字
所有這些字面值都有一個(gè)十進(jìn)制值17:
let decimalInteger = 17
let binaryInteger = 0b10001 // 17 二進(jìn)制
let octalInteger = 0o21 // 17 八進(jìn)制
let hexadecimalInteger = 0x11 // 17 十六進(jìn)制
浮點(diǎn)字面值可以是十進(jìn)制(沒有前綴)或十六進(jìn)制(有0x前綴)阅羹。它們必須在小數(shù)點(diǎn)的兩邊都有一個(gè)數(shù)字(或十六進(jìn)制數(shù))。十進(jìn)制浮點(diǎn)數(shù)還可以有一個(gè)可選的指數(shù)教寂,由大寫或小寫e表示;十六進(jìn)制浮點(diǎn)數(shù)必須有一個(gè)指數(shù)捏鱼,用大寫或小寫p表示。
對(duì)于指數(shù)為exp的小數(shù)酪耕,底數(shù)乘以10exp:
- 1.25e2 意味著 1.25 x 102, 或 125.0.
- 1.25e-2 意味著 1.25 x 10-2, 或 0.0125.
對(duì)于指數(shù)為exp的十六進(jìn)制數(shù)导梆,底數(shù)乘以2exp:
- 0xFp2 意味著 15 x 22, 或 60.0.
- 0xFp-2 意味著 15 x 2-2, 或 3.75.
所有這些浮點(diǎn)文字的十進(jìn)制值都是12.1875:
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
數(shù)值文字可以包含額外的格式,以使它們更容易閱讀迂烁。整數(shù)和浮點(diǎn)數(shù)都可以用額外的零來填充看尼,并且可以包含下劃線來增強(qiáng)可讀性。兩種格式都不會(huì)影響文字的基本值:
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
8 數(shù)值類型轉(zhuǎn)換
對(duì)代碼中的所有通用整型常量和變量使用Int類型盟步,即使已知它們是非負(fù)的藏斩。在日常情況中使用默認(rèn)整數(shù)類型意味著整型常量和變量在代碼中立即可以互操作,并將匹配整型文字值的推斷類型却盘。
只有在手頭的任務(wù)特別需要其他整數(shù)類型時(shí)才使用它們狰域,因?yàn)橥獠吭达@式地調(diào)整了數(shù)據(jù)大小,或者為了性能谷炸、內(nèi)存使用或其他必要的優(yōu)化北专。在這些情況下使用顯式大小的類型有助于捕獲任何意外值溢出,并隱式記錄所使用數(shù)據(jù)的性質(zhì)旬陡。
8.1 整數(shù)轉(zhuǎn)換
可以存儲(chǔ)在整型常量或變量中的數(shù)字的范圍對(duì)于每種數(shù)值類型是不同的拓颓。Int8常量或變量可以存儲(chǔ)-128到127之間的數(shù)字,而UInt8常量或變量可以存儲(chǔ)0到255之間的數(shù)字描孟。當(dāng)你的代碼編譯時(shí)驶睦,一個(gè)不適合大小整數(shù)類型的常量或變量的數(shù)字被報(bào)告為一個(gè)錯(cuò)誤:
let cannotBeNegative: UInt8 = -1
// UInt8 cannot store negative numbers, and so this will report an error
let tooBig: Int8 = Int8.max + 1
// Int8 cannot store a number larger than its maximum value,
// and so this will also report an error
由于每個(gè)數(shù)值類型可以存儲(chǔ)不同范圍的值,因此必須根據(jù)具體情況選擇進(jìn)行數(shù)值類型轉(zhuǎn)換匿醒。這種可選擇的方法可以防止隱藏的轉(zhuǎn)換錯(cuò)誤场航,并有助于在代碼中顯式地顯示類型轉(zhuǎn)換意圖。
要將一種特定的數(shù)字類型轉(zhuǎn)換為另一種廉羔,可以使用現(xiàn)有值初始化所需類型的新數(shù)字溉痢。在下面的示例中,常數(shù)2000的類型為UInt16憋他,而常數(shù)1的類型為UInt8孩饼。它們不能直接相加,因?yàn)樗鼈儾皇峭活愋椭竦病O喾炊迫ⅲ@個(gè)例子調(diào)用UInt16(one)來創(chuàng)建一個(gè)新的初始化值為one的UInt16,并使用這個(gè)值代替原來的值:
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
因?yàn)樘砑拥膬蛇叕F(xiàn)在都是UInt16類型揪罕,所以允許添加梯码。輸出常數(shù)(twoThousandAndOne)被推斷為UInt16類型宝泵,因?yàn)樗莾蓚€(gè)UInt16值的和。
SomeType(ofInitialValue)是調(diào)用Swift類型的初始化器并傳入初始值的默認(rèn)方法轩娶。在后臺(tái)儿奶,UInt16有一個(gè)接受UInt8值的初始化器,因此這個(gè)初始化器用于從現(xiàn)有的UInt8生成一個(gè)新的UInt16鳄抒。但是廓握,您不能在這里傳入任何類型—它必須是UInt16提供初始化器的類型。擴(kuò)展包括擴(kuò)展現(xiàn)有類型以提供接受新類型(包括您自己的類型定義)的初始化器嘁酿。
8.2 整數(shù)和浮點(diǎn)數(shù)轉(zhuǎn)換
整數(shù)和浮點(diǎn)數(shù)類型之間的轉(zhuǎn)換必須明確:
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double
在這里,常量3的值用于創(chuàng)建一個(gè)類型為Double的新值男应,這樣加法的兩邊都是相同的類型闹司。如果不進(jìn)行此轉(zhuǎn)換,則不允許添加沐飘。
浮點(diǎn)到整數(shù)的轉(zhuǎn)換也必須明確游桩。整數(shù)類型可以用雙值或浮點(diǎn)值初始化:
let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int
以這種方式初始化新的整數(shù)值時(shí),浮點(diǎn)值總是被截?cái)嗄推印_@意味著4.75變成4借卧,而-3.9變成-3。
注意
組合數(shù)值常量和變量的規(guī)則與組合數(shù)值文字的規(guī)則不同筛峭。文字值3可以直接添加到文字值0.14159铐刘,因?yàn)閿?shù)字文字本身沒有顯式的in和of類型。它們的類型只有在由編譯器計(jì)算時(shí)才會(huì)推斷出來影晓。
9 類型別名
類型別名定義現(xiàn)有類型的替代名稱。可以通過typealias關(guān)鍵詞定義類型別名场躯。
當(dāng)你想通過上下文更合適的名稱來引用一個(gè)現(xiàn)有的類型時(shí)凯力,類型別名很有用,例如當(dāng)從外部來源處理特定大小的數(shù)據(jù)時(shí):
typealias AudioSample = UInt16
一旦定義類型別名饵婆,你可以在任何地方使用別名勺馆,在可能使用原始的名稱的地方:
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0
這里,AudioSample被定義為UInt16的別名侨核。因?yàn)樗且粋€(gè)別名草穆,調(diào)用AudioSample.min也就是調(diào)用UInt16.min,作為maxAmplitudeFound為0的值芹关。
10 布爾值
Swift有一個(gè)基本的布爾類型续挟,叫做Bool。布爾值被稱為邏輯值侥衬,因?yàn)樗鼈冎荒苁钦婊蚣偈觥wift提供了兩個(gè)布爾常量值true和false:
let orangesAreOrange = true
let turnipsAreDelicious = false
orangesAreOrange和turnipsAreDelicious的類型已經(jīng)被推斷為Bool跑芳,因?yàn)樗鼈兪怯貌紶栁淖种党跏蓟摹Ec上面的Int和Double一樣直颅,如果在創(chuàng)建常量或變量時(shí)就將它們設(shè)置為true或false博个,則不需要將它們聲明為Bool。當(dāng)Swift用其他已知類型的值初始化常量或變量時(shí)功偿,類型推斷有助于使Swift代碼更加簡潔和可讀盆佣。
布爾值在處理?xiàng)l件語句時(shí)特別有用,比如if語句:
if turnipsAreDelicious {
print("Mmm, tasty turnips!")
} else {
print("Eww, turnips are horrible.")
}
// Prints "Eww, turnips are horrible."
條件語句(如if語句)在控制流
中有更詳細(xì)的介紹械荷。
Swift的類型安全防止非布爾值被Bool替換共耍。下面的示例報(bào)告了編譯時(shí)錯(cuò)誤:
let i = 1
if i {
// this example will not compile, and will report an error
}
但是,下面的另一個(gè)例子是有效的:
let i = 1
if i == 1 {
// this example will compile successfully
}
i == 1比較的結(jié)果是Bool類型吨瞎,因此第二個(gè)示例通過了類型檢查痹兜。像i == 1這樣的比較在基本運(yùn)算符中討論。
與Swift中的其他類型安全示例一樣颤诀,這種方法避免了意外錯(cuò)誤字旭,并確保特定代碼段的意圖始終清晰。
11 元組
元組將多個(gè)值分組為一個(gè)復(fù)合值崖叫。元組中的值可以是任何類型遗淳,不必彼此具有相同的類型。
在本例中心傀,(404屈暗,“Not Found”)是描述HTTP狀態(tài)代碼的元組。HTTP狀態(tài)碼是web服務(wù)器在請求web頁面時(shí)返回的特殊值脂男。如果你請求的網(wǎng)頁不存在恐锦,它會(huì)返回404 Not Found狀態(tài)碼。
let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")
元組將一個(gè)Int和一個(gè)字符串組合在一起疆液,為HTTP狀態(tài)碼提供兩個(gè)單獨(dú)的值:一個(gè)數(shù)字和一個(gè)可讀的描述一铅。它可以被描述為“類型(Int, String)的元組”。
您可以根據(jù)類型的任意排列創(chuàng)建元組堕油,它們可以包含任意多的不同類型潘飘。沒有什么可以阻止您擁有類型(Int, Int, Int)或(String, Bool)的元組,或者您需要的任何其他置換掉缺。
您可以將元組的內(nèi)容分解為單獨(dú)的常量或變量卜录,然后像往常一樣訪問它們:
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// Prints "The status code is 404"
print("The status message is \(statusMessage)")
// Prints "The status message is Not Found"
如果你只需要一些元組的值,當(dāng)你分解元組的時(shí)候眶明,用一個(gè)下劃線(_)忽略部分元組:
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"
或者艰毒,使用從0開始的索引號(hào)訪問元組中的單個(gè)元素值:
print("The status code is \(http404Error.0)")
// Prints "The status code is 404"
print("The status message is \(http404Error.1)")
// Prints "The status message is Not Found"
當(dāng)元組被定義時(shí),您可以命名元組中的單個(gè)元素:
let http200Status = (statusCode: 200, description: "OK")
如果你在一個(gè)元組中命名元素搜囱,你可以使用元素名來訪問這些元素的值:
print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"
元組作為函數(shù)的返回值特別有用丑瞧。試圖檢索web頁面的函數(shù)可能會(huì)返回(Int, String)元組類型柑土,以描述頁面檢索的成功或失敗。通過返回具有兩個(gè)不同類型值的元組绊汹,與只能返回單個(gè)類型的單個(gè)值相比稽屏,函數(shù)提供了關(guān)于其結(jié)果的更有用的信息。有關(guān)更多信息西乖,請參見具有多個(gè)返回值的函數(shù)狐榔。
注意
元組對(duì)于相關(guān)值的簡單組非常有用。它們不適合創(chuàng)建復(fù)雜的數(shù)據(jù)結(jié)構(gòu)获雕。如果數(shù)據(jù)結(jié)構(gòu)可能更復(fù)雜薄腻,那么將其建模為類或結(jié)構(gòu)體,而不是元組届案。有關(guān)更多信息被廓,請參見結(jié)構(gòu)體和類。
12 可選值
在可能沒有值的情況下使用可選值萝玷。可選的表示兩種可能:要么有一個(gè)值昆婿,您可以展開可選的來訪問該值球碉,要么根本沒有值。
注意
可選值的概念在C或Objective-C中不存在仓蛆。Objective-C中最接近的是睁冬,從方法中返回nil的能力,否則會(huì)返回一個(gè)對(duì)象看疙,nil表示“缺少一個(gè)有效對(duì)象”豆拨。但是,這只適用于對(duì)象能庆,而不適用于結(jié)構(gòu)體施禾、基本C類型或枚舉值。對(duì)于這些類型搁胆,Objective-C方法通常返回一個(gè)特殊的值(比如NSNotFound)來表示沒有值弥搞。這種假設(shè)方法的調(diào)用者知道有一個(gè)特殊的值要進(jìn)行測試,并且記得檢查它渠旁。Swift的可選值可以讓你指示任何類型的值是否存在攀例,而不需要特殊的常量。
下面是一個(gè)示例顾腊,說明如何使用可選值來處理缺少值的情況粤铭。Swift的Int類型有一個(gè)初始化器,它試圖將字符串值轉(zhuǎn)換為Int值杂靶。但是梆惯,不是每個(gè)字符串都可以轉(zhuǎn)換為整數(shù)酱鸭。字符串“123”可以轉(zhuǎn)換為數(shù)字值123,但是字符串“hello, world”沒有明顯的數(shù)字值可以轉(zhuǎn)換加袋。
下面的例子使用了初始化器來嘗試將一個(gè)字符串轉(zhuǎn)換成Int類型:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"
因?yàn)槌跏蓟骺赡苁×堇保祷匾粋€(gè)可選的整型,而不是整型职烧。問號(hào)表示它包含的值是可選的扁誓,這意味著它可能包含一些Int值,也可能根本不包含值蚀之。它不能包含任何其他東西蝗敢,比如Bool值或String值。要么是整數(shù)寿谴,要么什么都不是)
12.1 nil
通過分配特殊值nil,設(shè)置一個(gè)可選的變量為無值狀態(tài):
var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value
注意
不能對(duì)非可選常量和變量使用nil失受。如果代碼中的常量或變量在某些條件下需要處理缺少值的情況讶泰,請始終將其聲明為適當(dāng)類型的可選值。
如果定義了一個(gè)可選變量拂到,并且沒有給它設(shè)值痪署,那么它的值默認(rèn)是nil:
var surveyAnswer: String?
// surveyAnswer is automatically set to nil
注意
Swift里的nil和OC里的不同,OC里的nil是一個(gè)指針兄旬,指向一個(gè)不存在的對(duì)象狼犯。Swift里的nil不是一個(gè)指針--它是某種類型的值的缺失。任何類型的選項(xiàng)都可以設(shè)置為nil领铐,而不僅僅是對(duì)象類型悯森。
12.2 If語句和強(qiáng)制解包
通過比較可選值和空值,可以使用if語句來確定可選值是否包含值绪撵。您可以使用“等于”操作符(==)或“不等于”操作符(!=)執(zhí)行比較瓢姻。
如果可選值有值,會(huì)被認(rèn)為不是nil:
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// Prints "convertedNumber contains some integer value."
一旦確定可選值有值音诈,可以通過在可選名稱的末尾添加感嘆號(hào)(!)來訪問其基礎(chǔ)值汹来。感嘆號(hào)有效地表示:“我知道這個(gè)可選值肯定有一個(gè)值;請使用它。這被稱為可選值的強(qiáng)制解包:
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
// Prints "convertedNumber has an integer value of 123."
想要了解更多If語句改艇,請參閱控制流收班。
注意
試著使用!訪問不存在的可選值將觸發(fā)運(yùn)行時(shí)錯(cuò)誤。在使用前谒兄,一定要確保一個(gè)可選的包含一個(gè)非nil值!強(qiáng)制打開它的值摔桦。
12.3 可選值綁定
您可以使用可選綁定來查明可選項(xiàng)是否包含值,如果包含值,則使該值作為臨時(shí)常量或變量可用邻耕∨缚В可選綁定可以與if和while語句一起使用,以檢查可選語句中的值兄世,并將該值提取為常量或變量啼辣,這是單個(gè)操作的一部分。if和while語句在控制流中有更詳細(xì)的描述御滩。
使用If語句寫一個(gè)如下的可選綁定:
if let constantName = someOptional {
statements
}
您可以重寫Optionals部分中的possibleNumber示例鸥拧,以使用可選綁定而不是強(qiáng)制解包:
if let actualNumber = Int(possibleNumber) {
print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
print("The string \"\(possibleNumber)\" could not be converted to an integer")
}
// Prints "The string "123" has an integer value of 123"
這段代碼的意思是:
“如果Int(possibleNumber)返回的可選整數(shù)包含一個(gè)值,則設(shè)置一個(gè)名為actualNumber的新常量為可選整數(shù)中包含的值削解「幌遥”
如果轉(zhuǎn)換成功,就可以在If語句的第一個(gè)分支中使用actualNumber常數(shù)氛驮。它已經(jīng)用可選項(xiàng)中包含的值進(jìn)行了初始化腕柜,因此不需要使用!后綴來訪問其值。在本例中矫废,actualNumber只是用于打印轉(zhuǎn)換的結(jié)果盏缤。
可以使用可選綁定的常量和變量。如果您想在If語句的第一個(gè)分支中操作actualNumber的值蓖扑,則可以編寫If var actualNumber唉铜,而可選語句中包含的值將作為變量而不是常量可用。
您可以在一個(gè)if語句中包含任意多的可選綁定和布爾條件赵誓,用逗號(hào)分隔。如果可選綁定中的任何值為nil或任何布爾值條件為false柿赊,則整個(gè)If語句的條件被認(rèn)為為false俩功。下面的if語句是等價(jià)的:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// Prints "4 < 42 < 100"
注意
在if語句中使用可選綁定創(chuàng)建的常量和變量只能在if語句體中使用。相反碰声,使用guard語句創(chuàng)建的常量和變量可以在guard語句后面的代碼行中使用诡蜓,如Early Exit中所述。
12.4 隱式解包可選值
如上所述胰挑,可選值表示常量或變量允許“無值”蔓罚。可選值可以用if語句檢查值是否存在瞻颂,也可以用可選綁定有條件地解除包裝豺谈,以訪問可選值(如果存在)。
有時(shí)候從程序結(jié)構(gòu)上贡这,可選值在第一次設(shè)置后茬末,總會(huì)有一個(gè)值。在這些情況下,在訪問可選值的時(shí)候丽惭,移除檢查和解包的操作是有效的击奶,因?yàn)樗偸前踩丶俣ㄓ幸粋€(gè)值。
這些類型的可選值被定義為隱式的解包可選值责掏。通過在希望使類型可選的后面放置感嘆號(hào)(String!)而不是問號(hào)(String?)來編寫可選的隱式解包柜砾。在使用可選名稱時(shí),不要在其后放置感嘆號(hào)换衬,而應(yīng)在聲明可選類型時(shí)在其后放置感嘆號(hào)痰驱。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation point
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation point
(注:這里終于清楚地明白了?和!的用法冗疮,說明學(xué)一門語言還是要從官方文檔開始)
你可以將可選的隱式解包看作允許可選的對(duì)象在需要時(shí)強(qiáng)制解包萄唇。當(dāng)你使用隱式解包裝可選值時(shí),Swift首先嘗試將其作為普通可選值使用;如果不能作為可選值使用术幔,Swift強(qiáng)制打開該值另萤。在上面的代碼中,可選值assumedString在將其值分配給implicitString之前被強(qiáng)制解除包裝诅挑,因?yàn)閕mplicitString具有顯式的四敞、非可選的字符串類型。在下面的代碼中拔妥,optionalString沒有顯式類型忿危,所以它是一個(gè)普通的可選類型。
let optionalString = assumedString
// The type of optionalString is "String?" and assumedString isn't force-unwrapped.
如果一個(gè)可選的隱式解包是nil没龙,并且您嘗試訪問它的包裝值铺厨,您將觸發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤。其結(jié)果與在不包含值的普通可選項(xiàng)后放置感嘆號(hào)完全相同硬纤。
你可以檢查一個(gè)可選的隱式解包是否為nil解滓,就像你檢查一個(gè)普通的可選:
if assumedString != nil {
print(assumedString!)
}
// Prints "An implicitly unwrapped optional string."
你也可以使用可選值綁定隱式解包可選的值,在一個(gè)單獨(dú)的語句中檢查和解包它的值:
if let definiteString = assumedString {
print(definiteString)
}
// Prints "An implicitly unwrapped optional string."
注意
當(dāng)一個(gè)變量可能在以后變成nil時(shí)筝家,不要使用可選值綁定隱式解包可選的值洼裤。如果需要在變量的生命周期內(nèi)檢查nil值,請始終使用普通的可選類型溪王。
13 錯(cuò)誤處理
使用錯(cuò)誤處理來響應(yīng)程序在執(zhí)行過程中可能遇到的錯(cuò)誤條件腮鞍。
可選值可以使用值的存在或不存在來傳遞函數(shù)的成功或失敗,與之相反莹菱,錯(cuò)誤處理允許您確定失敗的根本原因移国,并在必要時(shí)將錯(cuò)誤傳播到程序的另一部分。
當(dāng)函數(shù)遇到錯(cuò)誤條件時(shí)道伟,它會(huì)拋出錯(cuò)誤桥狡。然后,函數(shù)的調(diào)用者可以捕獲錯(cuò)誤并作出適當(dāng)?shù)捻憫?yīng)。
func canThrowAnError() throws {
// this function may or may not throw an error
}
函數(shù)通過在其聲明中包含throws關(guān)鍵字來表示可以拋出錯(cuò)誤裹芝。當(dāng)調(diào)用可能拋出錯(cuò)誤的函數(shù)時(shí)部逮,應(yīng)在表達(dá)式前添加try關(guān)鍵字。
Swift自動(dòng)將錯(cuò)誤傳播到當(dāng)前范圍之外嫂易,直到它們被catch子句處理兄朋。
do {
try canThrowAnError()
// no error was thrown
} catch {
// an error was thrown
}
do語句創(chuàng)建一個(gè)新的包含范圍,允許將錯(cuò)誤傳播到一個(gè)或多個(gè)catch子句怜械。
下面是一個(gè)例子颅和,說明如何使用錯(cuò)誤處理來響應(yīng)不同的錯(cuò)誤條件:
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
在本例中,如果沒有干凈的盤子或缺少任何配料缕允,makeASandwich()函數(shù)將拋出一個(gè)錯(cuò)誤峡扩。因?yàn)閙akeASandwich()可能拋出錯(cuò)誤,所以函數(shù)調(diào)用被包裝在一個(gè)try表達(dá)式中障本。通過將函數(shù)調(diào)用包裝在do語句中教届,拋出的任何錯(cuò)誤都將傳播到提供的catch子句。
如果沒有拋出錯(cuò)誤驾霜,則調(diào)用eatASandwich()函數(shù)案训。如果拋出一個(gè)錯(cuò)誤,并且它與sandwichError匹配粪糙。在outOfCleanDishes情況下强霎,則調(diào)用washDishes()函數(shù)。如果拋出一個(gè)錯(cuò)誤蓉冈,并且它與sandwichError匹配城舞。missingcomponents情況下,使用catch模式捕獲的相關(guān)[String]值調(diào)用buyGroceries(_:)函數(shù)寞酿。
錯(cuò)誤處理中更詳細(xì)地介紹了拋出家夺、捕獲和傳播錯(cuò)誤。
14 斷言和先決條件
斷言和前置條件是在運(yùn)行時(shí)進(jìn)行的檢查熟嫩。在執(zhí)行任何其他代碼之前秦踪,使用它們確保滿足必要條件褐捻。如果斷言或先決條件中的布爾值為真掸茅,則代碼執(zhí)行照常繼續(xù)。如果條件計(jì)算為false柠逞,則程序的當(dāng)前狀態(tài)無效;代碼執(zhí)行結(jié)束昧狮,應(yīng)用程序終止。
在編碼時(shí)板壮,可以使用斷言和先決條件來表示所作的假設(shè)和期望逗鸣,因此可以將它們作為代碼的一部分。斷言幫助您在開發(fā)期間發(fā)現(xiàn)錯(cuò)誤和不正確的假設(shè),而前提條件幫助您檢測生產(chǎn)中的問題撒璧。
除了在運(yùn)行時(shí)驗(yàn)證您的期望之外透葛,斷言和先決條件也成為代碼中有用的文檔形式。與上面錯(cuò)誤處理中討論的錯(cuò)誤條件不同卿樱,斷言和前置條件不會(huì)用于可恢復(fù)的或預(yù)期的錯(cuò)誤僚害。由于失敗的斷言或先決條件指示無效的程序狀態(tài),因此無法捕獲失敗的斷言繁调。
使用斷言和先決條件不能替代以不太可能出現(xiàn)無效條件的方式設(shè)計(jì)代碼萨蚕。但是,使用它們強(qiáng)制執(zhí)行有效的數(shù)據(jù)和狀態(tài)會(huì)導(dǎo)致應(yīng)用程序在出現(xiàn)無效狀態(tài)時(shí)可預(yù)測地終止蹄胰,并有助于使問題更容易調(diào)試岳遥。在檢測到無效狀態(tài)時(shí)立即停止執(zhí)行還有助于限制由該無效狀態(tài)造成的損害。
斷言和前提條件的區(qū)別在于它們被檢查的時(shí)候:斷言只在調(diào)試構(gòu)建中被檢查裕寨,但是前提條件在調(diào)試和生產(chǎn)構(gòu)建中都被檢查浩蓉。在生產(chǎn)構(gòu)建中,不計(jì)算斷言中的條件帮坚。這意味著您可以在開發(fā)過程中使用任意數(shù)量的斷言妻往,而不會(huì)影響生產(chǎn)中的性能。
14.1 用斷言調(diào)試
通過從Swift標(biāo)準(zhǔn)庫中調(diào)用assert(::file:line:)函數(shù)來編寫斷言试和。向該函數(shù)傳遞一個(gè)計(jì)算結(jié)果為true或false的表達(dá)式讯泣,以及一條消息,如果條件的結(jié)果為false阅悍,則顯示該消息好渠。例如:
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 is not >= 0.
在本例中,如果age >= 0的值為真(即age的值非負(fù))节视,則繼續(xù)執(zhí)行代碼拳锚。如果age的值是負(fù)數(shù),如上面的代碼所示寻行,那么age >= 0計(jì)算為false霍掺,斷言失敗,終止應(yīng)用程序拌蜘。
您可以省略斷言消息—例如杆烁,當(dāng)它只是以散文形式重復(fù)條件時(shí)。
assert(age >= 0)
如果代碼已經(jīng)檢查了條件简卧,那么可以使用assertionFailure(_:file:line:)函數(shù)來表示斷言失敗了兔魂。例如:
if age > 10 {
print("You can ride the roller-coaster or the ferris wheel.")
} else if age >= 0 {
print("You can ride the ferris wheel.")
} else {
assertionFailure("A person's age can't be less than zero.")
}
14.2 執(zhí)行先決條件
只要條件可能為假,但必須為真举娩,代碼才能繼續(xù)執(zhí)行析校,就使用前置條件构罗。例如,使用先決條件檢查下標(biāo)是否超出范圍智玻,或檢查函數(shù)是否傳遞了有效值遂唧。
你可以通過調(diào)用 precondition(_:_:file:line:)
函數(shù)來編寫一個(gè)先決條件。向該函數(shù)傳遞一個(gè)計(jì)算結(jié)果為true或false的表達(dá)式吊奢,以及一條消息蠢箩,如果條件的結(jié)果為false,則顯示該消息事甜。例如:
// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")
您還可以調(diào)用preconditionFailure(_:file:line:)函數(shù)來指示發(fā)生了故障——例如谬泌,如果采用了switch的默認(rèn)情況,但是所有有效的輸入數(shù)據(jù)應(yīng)該由switch的其他情況之一處理逻谦。
注意
如果以u(píng)nchecked模式(-Ounchecked)編譯掌实,則不會(huì)檢查前置條件。編譯器假設(shè)前提條件總是為真邦马,并相應(yīng)地優(yōu)化代碼贱鼻。然而,無論優(yōu)化設(shè)置如何滋将,fatalError(_:file:line:)函數(shù)總是會(huì)停止執(zhí)行邻悬。
您可以在原型和早期開發(fā)期間使用fatalError(_:file:line:)函數(shù)為尚未實(shí)現(xiàn)的功能創(chuàng)建存根,方法是編寫fatalError(“未實(shí)現(xiàn)”)作為存根實(shí)現(xiàn)随闽。因?yàn)榕c斷言或先決條件不同父丰,致命錯(cuò)誤從來沒有被優(yōu)化出來,所以可以肯定掘宪,如果遇到存根實(shí)現(xiàn)蛾扇,執(zhí)行總是會(huì)停止。
總結(jié)
通過這一章節(jié)的學(xué)習(xí)魏滚,可以了解Swift基礎(chǔ)的一些知識(shí)以及它特有的一些功能:
- 如何去定義常量和變量
- 如何添加注釋:和OC差不多镀首,不過比OC多了個(gè)"""注釋多行的符號(hào)
- 分號(hào)的作用:單行上寫多行代碼時(shí)需要加上
- 整型、浮點(diǎn)型和布爾值
- Swift特有的類型安全和類型推斷:這個(gè)真的很實(shí)用鼠次,可提高開發(fā)效率
- 數(shù)字字面值和數(shù)值類型轉(zhuǎn)換
- 類型別名
- 元組
- 可選值:?和!的用法更哄,以及可選綁定if-let的用法
- 錯(cuò)誤處理:try-catch和do-try-catch
- 斷言和先決條件
這里就不詳細(xì)說明各個(gè)知識(shí)點(diǎn)了,可以從上面的內(nèi)容中尋找答案腥寇。最后成翩,如果大家喜歡的話,可以給個(gè)star哦花颗,有你的支持捕传,就是我的動(dòng)力惠拭!
參考文檔:Swift - The Basics