基礎(chǔ)類型
雖然Swift是一個(gè)為開發(fā)iOS和OS X app設(shè)計(jì)的全新編程語言,但是Swift的很多特性還是跟C和Objective-C相似。
Swift也提供了與C和Objective-C類似的基礎(chǔ)數(shù)據(jù)類型,包括整形Int、浮點(diǎn)數(shù)Double和Float、布爾類型Bool以及字符串類型String。Swift還提供了兩種更強(qiáng)大的基本集合數(shù)據(jù)類型,Array和Dictionary,更詳細(xì)的內(nèi)容可以參考:Collection Types。
跟C語言一樣,Swift使用特定的名稱來定義和使用變量。同樣,Swift中也可以定義常量,與C語言不同的是,Swift中的常量更加強(qiáng)大,在編程時(shí)使用常量能夠讓代碼看起來更加安全和簡(jiǎn)潔。
除了常見的數(shù)據(jù)類型之外,Swift還集成了Objective-C中所沒有的“元組”類型,可以作為一個(gè)整體被傳遞。元組也可以成為一個(gè)函數(shù)的返回值,從而允許函數(shù)一次返回多個(gè)值。
Swift還提供了可選類型,用來處理一些未知的不存在的值??蛇x類型的意思是:這個(gè)值要么存在,并且等于x,要么根本不存在??蛇x類型類似于Objective-C中指針的nil值,但是nil只對(duì)類(class)有用,而可選類型對(duì)所有的類型都可用,并且更安全??蛇x類型是大部分Swift新特性的核心。
可選性類型只是Swift作為類型安全的編程語言的一個(gè)例子。Swift可以幫助你更快地發(fā)現(xiàn)編碼中的類型錯(cuò)誤。如果你的代碼期望傳遞的參數(shù)類型是String的,那么類型安全就會(huì)防止你錯(cuò)誤地傳遞一個(gè)Int值。這樣就可以讓編程人員在開發(fā)期更快地發(fā)現(xiàn)和修復(fù)問題。
常量和變量
常量和變量由一個(gè)特定名稱來表示,如maximumNumberOfLoginAttempt 或者 welcomeMessage。常量所指向的是一個(gè)特定類型的值,如數(shù)字10或者字符”hello”。變量的值可以根據(jù)需要不斷修改,而常量的值是不能夠被二次修改的。
常量和變量的聲明
常量和變量在使用前都需要聲明,在Swift中使用let關(guān)鍵詞來聲明一個(gè)常量,var關(guān)鍵詞聲明一個(gè)變量。如下面例子:
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
以上代碼可以理解為:
聲明一個(gè)叫maximumNumberOfLoginAttempts的值為10的常量。然后聲明一個(gè)變量currentLoginAttempt初始值為0。
在這個(gè)例子中,最大的登錄嘗試次數(shù)10是不變的,因此聲明為常量。而已經(jīng)登錄的嘗試次數(shù)是可變的,因此定義為變量。也可以在一行中聲明多個(gè)變量或常量,用,號(hào)分隔:
var x = 0.0, y = 0.0, z = 0.0
注:如果一個(gè)值在之后的代碼中不會(huì)再變化,應(yīng)該用let關(guān)鍵詞將它聲明為常量。變量只用來存儲(chǔ)會(huì)更改的值。
類型注解
在聲明常量和變量時(shí),可以使用注解來注明該變量或常量的類型。使用:號(hào)加空格加類型名在變量或常量名之后就可以完成類型注解。下面的例子就是聲明了一個(gè)變量叫welcomeMessage,注解類型為字符串String:
var welcomeMessage: String
分號(hào) “:” 在這的作用就像是在說:…是…類型的,因此上述代碼可以理解為:
聲明一個(gè)叫welcomeMessage的變量,它的類型是String
這個(gè)類型注解表明welcomeMessage變量能無誤地存儲(chǔ)任何字符串類型的值,比如welcomeMessage = “hello”
注:實(shí)際編程中很少需要使用類型注解,定義常量或者變量的時(shí)候Swift已經(jīng)根據(jù)初始化的值確定了類型信息。Swift幾乎都可以隱式的確定變量或常量的類型,詳見: Type Safety and Type Inference。而上面的welcomeMessage的例子中,初始化值沒有被給出,所以更好的辦法是指定welcomeMessage變量的類型而不是讓Swift隱式推導(dǎo)類型。
常量和變量的命名
Swift中可以使用幾乎任何字符來作為常量和變量名,包括Unicode,比如:
let π = 3.14159
let 你好 = "你好世界"
let = "dogcow"
但是名稱中不能含有數(shù)學(xué)符號(hào),箭頭,無效的Unicode,橫線-和制表符,且不能以數(shù)字開頭,盡管數(shù)字可以包含在名稱里。一旦完成了聲明,就不能再次聲明相同名稱的變量或常量,或者改變它的類型。變量和常量也不能互換。
注:如果你想用Swift保留字命名一個(gè)常量或者變量,你可以用 ` 符號(hào)把命名包圍起來。盡管如此,除非處于特別的意圖,盡量不要使用保留字作為變量/常量名。
可以改變變量的值為它聲明的類型的其它值,如下的例子里,變量friendlyWelcome的值從“Hello!”被修改為”Bonjour!”:
var friendlyWelcome = “hello!”
friendlyWelcome = “Bonjour!”
// friendlyWelcome is now “Bonjour!”
與變量不同的是,常量的值一旦確定就不能修改。如果想嘗試改變一個(gè)常量的值,編譯代碼時(shí)就會(huì)報(bào)錯(cuò)
let languageName = "Swift"
languageName = "Swift++"
// this is a compile-time error - languageName cannot be changed
輸出常量和變量
Swift使用println來輸出變量或者常量:
println(friendlyWelcome)
// prints “Bonjour!”
println是一個(gè)全局函數(shù),用來輸出一個(gè)值,最后輸出一個(gè)換行。在Xcode中,println輸出在控制臺(tái)中。print函數(shù)也類似,只不過最后不會(huì)輸出換行。
println函數(shù)一般輸出一個(gè)字符串
println("This is a string")
// prints "This is a string"
println函數(shù)還可以格式化輸出一些日志信息,就像是Cocoa中NSLog函數(shù)的行為一樣,可以包括一些常量和變量本身。Swift在字符串中插入變量名作為占位符,使用反斜杠()來提示Swift替換變量/常量名為其實(shí)際的值,如:
println(“The current value of friendlyWelcome is (friendlyWelcome)”) // prints “The current value of friendlyWelcome is Bonjour!”
注:關(guān)于格式化字符的詳見 String Interpolation
注釋
不參與編譯的語句稱為注釋,注釋可以提示你代碼的意圖。Swift中的注釋和C語言中的一樣,有單行注釋
//this is a comment
和多行注釋,使用/和/分隔
/* this is also a comment,
but written over multiple lines */
和C語言不同的是,多行注釋可以嵌套,你需要先開始一個(gè)多行注釋,然后開始第二個(gè)多行注釋,關(guān)閉注釋的時(shí)候先關(guān)閉第二個(gè),然后是第一個(gè)。如下
/* 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 */
這樣可以方便地在大段已注釋的代碼塊中繼續(xù)添加注釋
分號(hào)
和其它一些編程語言不同,Swift不需要使用分號(hào) ; 來分隔每一個(gè)語句。當(dāng)然你也可以選擇使用分號(hào),或者你想在一行中書寫多個(gè)語句。
let cat = ""; println(cat)
// prints ""
整數(shù)
整數(shù)就是像42和-23這樣不帶分?jǐn)?shù)的數(shù)字,包括有符號(hào)(正數(shù),負(fù)數(shù),0)和無符號(hào)(正數(shù),0)。Swift提供了8、16、32和64位的數(shù)字形式,和C語言類似,可以使用8位的無符號(hào)整數(shù)UInt8,或者32位的整數(shù)Int32.像其他Swift類型一樣,這些類型名的首字母大寫。
整數(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
這些值邊界值區(qū)分了整數(shù)的類型(比如UInt8),所以可以像該類型的其他值一樣被用在表達(dá)式中而不用考慮益處的問題。
Int類型
一般來說,編程人員在寫代碼時(shí)不需要選擇整數(shù)的位數(shù),Swift提供了一種額外的整數(shù)類型Int,是和當(dāng)前機(jī)器環(huán)境的字長(zhǎng)相同的整數(shù)位數(shù)
1.在32位機(jī)器上,Int和Int32一樣大小
2.在64位機(jī)器上,Int和Int64一樣大小
除非你確實(shí)需要使用特定字長(zhǎng)的正數(shù),盡量使用Int類型。這保證了代碼的可移植性。即使在32位的平臺(tái)上,Int也可以存儲(chǔ)-2,147,483,648 到2,147,483,647范圍內(nèi)的值,這對(duì)大部分正數(shù)來講已經(jīng)足夠了。
UInt類型
Swift還提供了一種無符號(hào)類型UInt,同理也是和當(dāng)前機(jī)器環(huán)境的字長(zhǎng)相等。
1.在32位機(jī)器上,UInt和UInt32一樣大小
2.在64位機(jī)器上,UInt和UInt64一樣大小
注:只有顯式的需要指定一個(gè)長(zhǎng)度跟機(jī)器字長(zhǎng)相等的無符號(hào)數(shù)的時(shí)候才需要使用UInt,其他的情況,盡量使用Int,即使這個(gè)變量確定是無符號(hào)的。都使用Int保證了代碼的可移植性,避免了不同數(shù)字類型之間的轉(zhuǎn)換。詳見Type Safety and Type Inference.
5、浮點(diǎn)數(shù)
浮點(diǎn)數(shù)就是像3.14159,0.1,-273.15這樣帶分?jǐn)?shù)的數(shù)字。浮點(diǎn)數(shù)可以表達(dá)比Int范圍更廣(更大或更?。┑臄?shù)值。swift支持兩種帶符號(hào)浮點(diǎn)數(shù)類型:
1.Double類型能表示64位的有符號(hào)浮點(diǎn)數(shù)。當(dāng)需要表的數(shù)字非常大或者精度要求非常高的時(shí)候可以使用Double類型。
2.Float類型能表示32為的有符號(hào)浮點(diǎn)數(shù)。當(dāng)需要表達(dá)的數(shù)字不需要64位精度的時(shí)候可以使用Float類型。
注 Double 至少有15位小數(shù),F(xiàn)loat至少有6位小數(shù)。合適的浮點(diǎn)數(shù)小數(shù)位數(shù)取決于你代碼里需要處理的浮點(diǎn)數(shù)范圍。
6、類型安全和類型推導(dǎo)
Swift是一種類型安全的語言。類型安全就是說在編程的時(shí)候需要弄清楚變量的類型。如果您的代碼部分需要一個(gè)字符串,你不能錯(cuò)誤地傳遞一個(gè)整數(shù)類型。
因?yàn)镾wift是類型安全的,它會(huì)在編譯的時(shí)候就檢查你的代碼,任何類型不匹配時(shí)都會(huì)報(bào)錯(cuò)。這使得編程人員能夠盡快捕獲并盡可能早地在開發(fā)過程中修正錯(cuò)誤。
類型檢查可以在使用不同類型的值時(shí)幫助避免錯(cuò)誤。但是,這并不意味著你必須指定每一個(gè)常量和變量所聲明的類型。如果不指定你需要的類型,Swift使用類型推導(dǎo)來指定出相應(yīng)的類型。類型推導(dǎo)使編譯器在編譯的時(shí)候通過你提供的初始化值自動(dòng)推導(dǎo)出特定的表達(dá)式的類型。
類型推導(dǎo)使Swift比起C或Objective-C只需要更少的類型聲明語句。常量和變量仍然顯式類型,但大部分指定其類型的工作Swift已經(jīng)為你完成了。
當(dāng)你聲明一個(gè)常量或變量并給出初始值類型的時(shí)候,類型推導(dǎo)就顯得特別有用。這通常是通過給所聲明的常量或變量賦常值來完成的。 (常值是直接出現(xiàn)在源代碼中的值,如下面的例子42和3.14159 。 )
例如,如果您指定42到一個(gè)新的常數(shù)變量,而不用說它是什么類型,Swift推斷出你想要的常量是一個(gè)整數(shù),因?yàn)槟阋呀?jīng)初始化它為一個(gè)整數(shù)
let meaningOfLife= 42
// meaningOfLife is inferred to be of typeInt
同樣,如果你不指定浮點(diǎn)值的類型,Swift推斷出你想要?jiǎng)?chuàng)建一個(gè)Double:
let pi = 3.14159
// pi is inferred to be of type Double
Swift總是選擇Double(而非Float)當(dāng)它需要浮點(diǎn)數(shù)類型時(shí)。如果你在一個(gè)表達(dá)式中把整數(shù)和浮點(diǎn)數(shù)相加,會(huì)推導(dǎo)一個(gè)Double類型:
let anotherPi= 3 + 0.14159
// anotherPi is also inferred to be of typeDouble
常值3沒有顯示指明類型,所以Swift根據(jù)表達(dá)式中的浮點(diǎn)值推出輸出類型Double。
數(shù)值量表達(dá)
整型常量可以寫成:
1.一個(gè)十進(jìn)制數(shù),不帶前綴
2.一個(gè)二進(jìn)制數(shù),用前綴0b
3.一個(gè)八進(jìn)制數(shù),用0o前綴
4.一個(gè)十六進(jìn)制數(shù),以0x前綴
所有如下用這些整型常量都可以來表達(dá)十進(jìn)制值的17:
let decimalInteger= 17
let binaryInteger = 0b10001 // 17 in binary notation
let octalInteger = 0o21 // 17 in octal notation
let hexadecimalInteger = 0x11 // 17 inhexadecimal notation
浮點(diǎn)可以是十進(jìn)制(不帶前綴)或十六進(jìn)制(以0x前綴),小數(shù)點(diǎn)的兩側(cè)必須始終有一個(gè)十進(jìn)制數(shù)(或十六進(jìn)制數(shù))。他們也可以有一個(gè)可選的指數(shù),由一個(gè)大寫或小寫e表示十進(jìn)制浮點(diǎn)數(shù)表示,或大寫/小寫p表示十六進(jìn)制浮點(diǎn)數(shù)
帶指數(shù)exp的十進(jìn)制數(shù),實(shí)際值等于基數(shù)乘以10的exp次方,如:
◎1.25e2表示1.25×102,或者125.0.
◎1.25e-2表示1.25×10-2,或者0.0125.
帶指數(shù)exp的十六進(jìn)制數(shù),實(shí)際值等于基部數(shù)乘以2的exp次方,如:
◎0xFp2表示15×22,或者60.0.
◎0xFp-2表示15×2-2,或者3.75.
所有如下這些浮點(diǎn)常量都表示十進(jìn)制的12.1875:
let decimalDouble= 12.1875
let exponentDouble= 1.21875e1
let hexadecimalDouble= 0xC.3p0
數(shù)值類型轉(zhuǎn)換
為代碼中所有通用的數(shù)值型整型常量和變量使用Int類型,即使它們已知是非負(fù)的。這意味著代碼中的數(shù)值常量和變量能夠相互兼容并且能夠與自動(dòng)推導(dǎo)出的類型相互匹配。
只有因?yàn)槟承┰颍ㄐ阅?,?nèi)存占用或者其他必須的優(yōu)化)確實(shí)需要使用其他數(shù)值類型的時(shí)候,才應(yīng)該使用這些數(shù)值類型。這些情況下使用顯式指定長(zhǎng)度的類型有助于發(fā)現(xiàn)值范圍溢出,同時(shí)應(yīng)該留下文檔。
整數(shù)轉(zhuǎn)換
可以存儲(chǔ)在一個(gè)整數(shù)常量或變量的范圍根據(jù)每個(gè)數(shù)值類型是不同的。一個(gè)Int8常量或變量可以存儲(chǔ)范圍-128到127之間的數(shù),而一個(gè)UInt8常量或變量可以存儲(chǔ)0到255之間的數(shù)字。錯(cuò)誤的賦值會(huì)讓編譯器報(bào)錯(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 thanits maximum value,
// and so this will also report an error
因?yàn)槊總€(gè)數(shù)字類型可以存儲(chǔ)不同范圍的值,你必須在基礎(chǔ)數(shù)值類型上逐步做轉(zhuǎn)換。這種可以防止隱藏的轉(zhuǎn)換錯(cuò)誤,并幫助明確你的代碼中類型轉(zhuǎn)換的意圖。
要轉(zhuǎn)換一個(gè)特定的數(shù)字類型到另一個(gè),你需要定義一個(gè)所需類型的新變量,并用當(dāng)前值初始化它。在下面的例子中,常量twoThousand是UInt16類型的,而常量one是UINT8類型的。它們不能被直接相加的,因?yàn)轭愋筒煌?。相反的,??示例調(diào)用UInt16(one)來創(chuàng)建一個(gè)用變量one的值初始化的UInt16類型的新變量,并且使用這個(gè)值來代替原來的值參與運(yùn)算:
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne= twoThousand + UInt16(one)
可以由于相加雙方的類型都是UInt16的,現(xiàn)在可以做加法運(yùn)算了。輸出常量(twoThousandAndOne)被推斷為UInt16類型的,因?yàn)樗莾蓚€(gè)UInt16的值的總和。
SomeType(ofInitialValue)是Swift默認(rèn)的類型轉(zhuǎn)換方式。實(shí)現(xiàn)上看,UInt16的有一個(gè)接受UINT8值的構(gòu)造器,這個(gè)構(gòu)造器用于從現(xiàn)有UInt8構(gòu)造出一個(gè)新的UInt16的變量。你不能傳入任意類型的參數(shù),它必須是一個(gè)類型的UInt16初始化能接受的類型。如何擴(kuò)展現(xiàn)有類型,規(guī)定接受新的類型(包括你自己的類型定義)可以參見Extensions。
整數(shù)和浮點(diǎn)數(shù)轉(zhuǎn)換
整數(shù)和浮點(diǎn)類型之間的轉(zhuǎn)化必須顯式聲明:
let three = 3
let pointOneFourOneFiveNine= 0.14159
let pi = Double(three) +pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to beof typde Double
這里,常量three的值被用來創(chuàng)建Double類型的新變量,從而使表達(dá)式兩側(cè)是相同的類型。如果沒有這個(gè)轉(zhuǎn)換,加法操作不會(huì)被允許。反之亦然,一個(gè)整數(shù)類型可以用double或float值進(jìn)行初始化:
let integerPi= Int(pi)
// integerPi equals 3, and is inferred tobe of type Int
當(dāng)使用這種方式初始化一個(gè)新的整數(shù)值的時(shí)候,浮點(diǎn)值總是被截?cái)?。這意味著,4.75變?yōu)?,和-3.9變?yōu)?3。
注:數(shù)值類型常量/變量的類型轉(zhuǎn)換規(guī)則和數(shù)字類型常值的轉(zhuǎn)換規(guī)則不同。常值3可以直接與常值0.14159相加,因?yàn)槌V禌]有一個(gè)明確的類型。他們的類型是被編譯器推導(dǎo)出來的。
類型別名
類型別名為現(xiàn)有類型定義的可替代名稱。你可以使用typealias關(guān)鍵字定義類型別名。類型別名可以幫助你使用更符合上下文語境的名字來指代一個(gè)已存在的類型,比如處理一個(gè)外來的有指定長(zhǎng)度的類型的時(shí)候:
typealias AudioSample = UInt16
一旦你定義了一個(gè)類型別名,你可以在任何可能使用原來的名稱地方使用別名:
var maxAmplitudeFound= AudioSample.min
// maxAmplitudeFound is now 0
這里,AudioSample被定義為一個(gè)UInt16的別名。因?yàn)樗且粋€(gè)別名,調(diào)用AudioSample.min實(shí)際上是調(diào)用UInt16.min,從而給maxAmplitudeFound變量賦初始值0。
布爾類型
Swift中的布爾類型使用Bool定義,也被稱為L(zhǎng)ogical(邏輯)類型,可選值是true和false:
let orangesAreOrange = true
let turnipsAreDelicious = false
這里 orangesAreOrange和turnipsAreDelicious的類型被推導(dǎo)為Bool 因?yàn)樗麄儽怀跏蓟籅ool類型的常值。跟Int和Double類型一樣,在定義布爾類型的時(shí)候不需要顯式的給出數(shù)據(jù)類型,只需要直接賦值為true或false即可 。當(dāng)使用確定類型的常值初始化一個(gè)常量/變量的時(shí)候,類型推導(dǎo)使Swift代碼更精確和可讀。 布爾類型在條件語句中特別適用,比如在if語句中
if turnipsAreDelicious {
println("Mmm, tasty turnips!")
} else {
println("Eww, turnips are horrible.")
}
// prints "Eww, turnips are horrible."
像if語句這樣的條件語句,我們會(huì)在之后的章節(jié)ControlFlow有詳細(xì)介紹。 Swift的類型安全策略會(huì)防止其他非布爾類型轉(zhuǎn)換為布爾類型使用,比如
let i = 1
if i {
// this example will not compile, and will report an error
}
就會(huì)報(bào)錯(cuò),但這在其他編程語言中是可行的。但是如下的定義是正確的:
let i = 1
if i == 1 {
// this example will compile successfully
}
i == 1的結(jié)果就是一個(gè)布爾類型,所以可以通過類型檢查。像i==1這種比較將會(huì)在章節(jié)[Basic Operators]中討論。上面的例子也是一個(gè)Swift類型安全的例子。類型安全避免了偶然的類型錯(cuò)誤,保證了代碼的意圖是明確的。
元組類型
元組類型可以將一些不同的數(shù)據(jù)類型組裝成一個(gè)元素,這些數(shù)據(jù)類型可以是任意類型,并且不需要是同樣的類型。
在下面的例子中,(404, “Not Found”) 是一個(gè)HTTP狀態(tài)碼。HTTP狀態(tài)碼是請(qǐng)求網(wǎng)頁的時(shí)候返回的一種特定的狀態(tài)編碼。404錯(cuò)誤的具體含義是頁面未找到。
let http404Error = (404, “Not Found”) // http404Error is of type (Int, String), and equals (404, “Not Found”)
這個(gè)元組由一個(gè)Int和一個(gè)字符串String組成,這樣的組合即包含了數(shù)字,也包含了便于人們認(rèn)知的字符串描述。這個(gè)元組可以描述為類型(Int,String)的元組。
編程人員可以隨意地創(chuàng)建自己需要的元組類型,比如 (Int, Int, Int), 或者(String, Bool)等。同時(shí)組成元組的類型數(shù)量也是不限的。 可以通過如下方式分別訪問一個(gè)元組的值:
let (statusCode, statusMessage) = http404Error
println("The status code is /(statusCode)")
// prints "The status code is 404"
println("The status message is /(statusMessage)")
// prints "The status message is Not Found"
如果僅需要元組中的個(gè)別值,可以使用(_)來忽略不需要的值
let (justTheStatusCode, _) = http404Error
println("The status code is /(justTheStatusCode)")
// prints "The status code is 404"
另外,也可以使用元素序號(hào)來選擇元組中的值,注意序號(hào)是從0開始的
println("The status code is /(http404Error.0)")
// prints "The status code is 404"
println("The status message is /(http404Error.1)")
// prints "The status message is Not Found"
在創(chuàng)建一個(gè)元組的時(shí)候,也可以直接指定每個(gè)元素的名稱,然后直接使用元組名.元素名訪問,如:
let http200Status = (statusCode: 200, description: "OK")
println("The status code is /(http200Status.statusCode)")
// prints "The status code is 200"
println("The status message is /(http200Status.description)")
// prints "The status message is OK"
元組類型在作為函數(shù)返回值的時(shí)候特別適用,可以為函數(shù)返回更多的用戶需要的信息。比如一個(gè)請(qǐng)求web頁面的函數(shù)可以返回(Int,String)類型的元組來表征頁面獲取的成功或者失敗。返回兩個(gè)不同類型組成的元組可以比只返回一個(gè)類型的一個(gè)值提供更多的返回信息。詳見Functions with Multiple Return Values
可選類型
在一個(gè)值可能不存在的時(shí)候,可以使用可選類型。這種類型的定義是:要么存在這個(gè)值,且等于x,要么在這個(gè)值 不存在。
注:這種類型在C和Objective-C中是不存在的,但是Objective-C中有一個(gè)相似的類型,叫nil,但是僅僅對(duì)對(duì)象有用。對(duì)其他的情況,Object-C方法返回一個(gè)特殊值(比如NSNotFound)來表明這個(gè)值不存在。這種方式假設(shè)方法調(diào)用者知道這個(gè)特殊值的存在和含義。Swift的可選類型幫助你定義任意的值不存在的情況。
下面給出一個(gè)例子,在Swift中String類型有一個(gè)叫toInt的方法,能夠?qū)⒁粋€(gè)字符串轉(zhuǎn)換為一個(gè)Int類型。但是需要注意的是,不是所有的字符串都可以轉(zhuǎn)換為整數(shù)。比如字符串”123″可以轉(zhuǎn)換為123,但是”hello, world”就不能被轉(zhuǎn)換。
let possibleNumber = "123"
let convertedNumber = possibleNumber.toInt()
// convertedNumber is inferred to be of type "Int?", or "optional Int"
由于toInt方法可能會(huì)失敗,因此它會(huì)返回一個(gè)可選的Int類型,而不同于Int類型。一個(gè)可選的Int類型被記為Int?,不是Int。問號(hào)表明它的值是可選的,可能返回的是一個(gè)Int,或者返回的值不存在。
if語句和強(qiáng)制解包
編程人員可以使用if語句來檢測(cè)一個(gè)可選類型是否包含一個(gè)特定的值,如果一個(gè)可選類型確實(shí)包含一個(gè)值,在if語句中它將返回true,否則返回false。如果你已經(jīng)檢測(cè)確認(rèn)該值存在,那么可以使用或者輸出它,在輸出的時(shí)候只需要在名稱后面加上感嘆號(hào)(!)即可,意思是告訴編譯器:我已經(jīng)檢測(cè)好這個(gè)值了,可以使用它了。如:
if convertedNumber {
println("/(possibleNumber) has an integer value of /(convertedNumber!)")
} else {
println("/(possibleNumber) could not be converted to an integer")
}
// prints "123 has an integer value of 123"
像if語句這樣的條件語句,我們會(huì)在之后的章節(jié)ControlFlow有詳細(xì)介紹。
選擇綁定
選擇綁定幫助確定一個(gè)可選值是不是包含了一個(gè)值,如果包含,把該值轉(zhuǎn)化成一個(gè)臨時(shí)常量或者變量。選擇綁定可以用在if或while語句中,用來在可選類型外部檢查是否有值并提取可能的值。if和while語句詳見ControlFlow。
方法如下:
if let constantName = someOptional {
statements
}
那么上一個(gè)例子也可以改寫為:
if let actualNumber = possibleNumber.toInt() {
println("/(possibleNumber) has an integer value of /(actualNumber)")
} else {
println("/(possibleNumber) could not be converted to an integer")
}
// prints "123 has an integer value of 123"
上述代碼理解起來不難:如果possibleNumber.toInt 返回的這個(gè)可選Int類型包含一個(gè)值,那么定義一個(gè)常量actualNumber來等于這個(gè)值,并在 后續(xù)代碼中直接使用。
如果轉(zhuǎn)換是成功的,那么actualNumber常量在if的第一個(gè)分支可用,并且被初始化為可選類型包含的值,同時(shí)也不需要使用!前綴。這個(gè)例子里,actualNumber只是簡(jiǎn)單的被用來打印結(jié)果。
常量和變量都可以用來做可選綁定。如果你想要在if第一個(gè)分支修改actualNumber的值,可以寫成if var actualNumber, actualNumber就成為一個(gè)變量從而可以被修改。
nil
可以給可選類型指定一個(gè)特殊的值nil:
var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value
如果你定義了一個(gè)可選類型并且沒有給予初始值的時(shí)候,會(huì)默認(rèn)設(shè)置為nil
var surveyAnswer: String? // surveyAnswer is automatically set to nil
注: Swift 的nil不同于Object-C中的nil. Object-C中,nil是一個(gè)指針指向不存在的對(duì)象。Swift中,nil不是指針而是一個(gè)特定類型的空值。任何類型的可選變量都可以被設(shè)為nil,不光是指針。
隱式解包可選類型
在上面的例子中,可選類型表示一個(gè)常量/變量可以沒有值??蛇x類型可以被if語句檢測(cè)是否有值,并且可以被可選綁定解包。
但是在一些情況下,可選類型是一直有效的,那么可以通過定義來隱式地去掉類型檢查,強(qiáng)制使用可選類型。這些可選類型被成為隱式解包的可選類型。你可以直接在類型后面加! 而不是?來指定。
隱式解包的可選類型主要用在一個(gè)變量/常量在定義瞬間完成之后值一定會(huì)存在的情況。這主要用在類的初始化過程中,詳見Unowned References and Implicitly Unwrapped Optional Properties.
隱式解包的可選類型本質(zhì)是可選類型,但是可以被當(dāng)成一般類型來使用,不需要每次驗(yàn)證值是否存在。如下的例子展示了可選類型和解包可選類型之間的區(qū)別。
let possibleString: String? = "An optional string."
println(possibleString!) // requires an exclamation mark to access its value
// prints "An optional string."
let assumedString: String! = "An implicitly unwrapped optional string."
println(assumedString) // no exclamation mark is needed to access its value
// prints "An implicitly unwrapped optional string."
你可以把隱式解包可選類型當(dāng)成對(duì)每次使用的時(shí)候自動(dòng)解包的可選類型。即不是每次使用的時(shí)候在變量/常量后面加!而是直接在定義的時(shí)候加。
注:如果一個(gè)隱式解包的可選類型不包含一個(gè)實(shí)際值,那么對(duì)它的訪問會(huì)拋出一個(gè)運(yùn)行時(shí)錯(cuò)誤。在變量/常量名后面加!的情況也是一樣的。
你依然可以把解包可選類型當(dāng)成正常的可選類型來探測(cè)是否有值。
if assumedString {
println(assumedString)
}
// prints "An implicitly unwrapped optional string."
或者通過選擇綁定檢查
if let definiteString = assumedString {
println(definiteString)
}
// prints "An implicitly unwrapped optional string."
注:如果一個(gè)可選類型存在沒有值的可能的話,不應(yīng)該使用解包可選類型。這種情況下,一定要使用正常的可選類型。
斷言
可選類型讓編程人員可以在運(yùn)行期檢測(cè)一個(gè)值是否存在,然后使用代碼來處理不存在的情況。但是有些情況下,如果一個(gè)值 不存在或者值不滿足條件會(huì)直接影響代碼的執(zhí)行,這個(gè)時(shí)候就需要使用斷言。這種情況下,斷言結(jié)束程序的執(zhí)行從而提供調(diào)試的依據(jù)。
使用斷言調(diào)試
斷言是一種實(shí)時(shí)檢測(cè)條件是否為true的方法,也就是說,斷言假定條件為true。斷言保證了后續(xù)代碼的執(zhí)行依賴于條件的成立。如果條件滿足,那么代碼繼續(xù)執(zhí)行,如果這個(gè)條件為false,那么代碼將會(huì)中斷執(zhí)行。
在Xcode中,在調(diào)試的時(shí)候如果中斷,可以通過查看調(diào)試語句來查看斷言失敗時(shí)的程序狀態(tài)。斷言也能提供適合的debug信息。 使用全局函數(shù)assert來使用斷言調(diào)試,assert函數(shù)接受一個(gè)布爾表達(dá)式和一個(gè)斷言失敗時(shí)顯示的消息,如:
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// this causes the assertion to trigger, because age is not >= 0
當(dāng)前一個(gè)條件返回false的時(shí)候,后面的錯(cuò)誤日志將會(huì)輸出。
在這個(gè)例子中,只有當(dāng)age >= 0的時(shí)候,條件被判定為true,但是age = -3,所以條件判定為false,輸出錯(cuò)誤日志 “A person's age cannot be less than zero”。
當(dāng)然錯(cuò)誤日志也可以省略,但是這樣不利于調(diào)試,如
assert(age >= 0)
使用斷言的時(shí)機(jī)
當(dāng)需要檢測(cè)一個(gè)條件可能是false,但是代碼運(yùn)行必須返回true的時(shí)候使用。下面給出了一些常用場(chǎng)景,可能會(huì)用到斷言檢測(cè):
◎傳遞一個(gè)整數(shù)類型下標(biāo)的時(shí)候,比如作為數(shù)組的Index,這個(gè)值可能太小或者太大,從而造成數(shù)組越界;
◎傳遞給函數(shù)的參數(shù),但是一個(gè)無效的參數(shù)將不能在該函數(shù)中執(zhí)行
◎一個(gè)可選類型現(xiàn)在是nil,但是在接下來的代碼中,需要是非nil的值才能夠繼續(xù)運(yùn)行。
詳見 Subscripts和Functions
注:斷言會(huì)導(dǎo)致程序運(yùn)行的中止,所以如果異常是預(yù)期可能發(fā)生的,那么斷言是不合適的。這種情況下,異常是更合適的。斷言保證錯(cuò)誤在開發(fā)過程中會(huì)被發(fā)現(xiàn),發(fā)布的應(yīng)用里最好不要使用。



















