Swift4.1第二章 The Basics

Swift語言開發指南

基礎部分

Swift是一門新的開發語言,它能夠在iOS、macOS watchOS以及tvOS系統環境下進行應用的開發。html

Swift提供了它本身的CObjective-C語言的全部基本數據類型。包括用於描述整數的Int,描述浮點型的DoubleFloat,描述布爾值的Bool以及描述文本數據的String。Swift也提供了三個主要的集合類型。好比集合類型中描述的的數組、集合、和字典。c++

和C語言同樣,Swift用變量來存儲和引用一個已經被聲明過名字的值。Swift一樣可以使用不可變數據類型的值。這些不能改變的值被稱爲常量,它比C語言中的常量更增強大。當你在使用不可變的值的時候,常量可以使你的代碼變得安全整潔。git

除了一些熟悉的類型以外,Swift還提供了Objective-C沒有的高級類型,好比說:元組。元組可以讓你創建立和傳遞一組數據。函數中返回多個值的時候你能夠用元組做爲單個複合值來接收。github

Swift也提供了一些可選類型,它可以處理值缺失的狀況。可選值的意思是說:這裏有一個值,它等於X或者它沒有值。使用可選類型的以後就像使用Objective-C中的空指針同樣,可是它的使用不只僅侷限於類,能夠是任意類型。和Objective-C中的空指針相比來講,可選類型可不只僅是安全和更具表現力那麼簡單,它們是Swift最強大功能中的核心。web

Swift是一門安全類型的語言。這意味着這門語言能夠幫你弄明白你所使用的值是什麼類型的。若是你的代碼中須要的是String,當你用Int來給它賦值的時候,類型安全會阻止你這麼作。一樣的,若是你意外的將可選字符串傳遞給非可選字符串那麼類型安全會阻止你這麼作。類型安全能夠幫你在開發過程當中儘早的捕獲和修正錯誤。編程

常量和變量

常量和變量都須要用一個別名(好比說maximumNumberOfLoginAttempts或者welcomeMessage)以及一個特殊的數據類型的值(好比說數字10和字符串hello)來來進行關聯.常量的值一旦被初始化設置了以後就不能發生改變了,而變量則能夠在將來給它賦不一樣的值。swift

常量和變量的聲明

常量和變量在使用以前都是要通過聲明的。你能夠用關鍵詞let來修飾一個常量,用var關鍵詞來修飾一個變量。下面的例子是經過對常量和變量的使用來模擬一個用戶嘗試登陸次數的場景。數組

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempts  = 0

這段代碼能夠被解讀爲:
聲明一個新的名字爲maximumNumberOfLoginAttempts的常量,它的值爲10。而後聲明一個新的名爲currentLoginAttempts的變量,它的初始值爲0.
在這個例子中,最大容許嘗試登陸次數被聲明爲一個常量,由於最大的嘗試登陸次數不能被修改。當前的嘗試登陸次數被聲明爲一個變量,由於這個次值會隨着嘗試登陸失敗的次數的增長而增長。
你能夠連續聲明幾個常量或者變量在同一行中,他們之間用逗號隔開。xcode

var x = 0.0,y = 0.0,z = 0.0

注意:
若是你的代碼中使用的值是不會發生改變的那麼用let關鍵字來聲明。若是聲明的值是須要發生改變的那麼用var關鍵字來聲明。安全

類型標註

在聲明常量和變量的時候你能夠爲它提供一個類型標註,它能夠很清楚的告訴編輯器常量和變量以什麼樣的數據類型被存儲。在常量和變量添加類型標註的時候在命名以後加一個冒號以以及空格,而後加上類型的名稱。

下面的這個例子裏給出一個叫作welcomeMessage變量的類型標註,它代表了這個變量被存儲爲一個String類型的值。

var welcomeMessage: String

在聲明的時候冒號的意思是「...類型的」,所以上面的代碼能夠這樣來理解:

聲明一個類型爲String的變量welcomeMessage

類型字符串的意思是能夠存儲任意值的字符串,能夠把它理解爲有存儲功能的"事物類型"。

變量welcomeMessage能夠被賦值爲任意的字符串而不報錯。

welcomeMessage = "Hello"

你能夠在一行定義多個同一種類型的變量,用逗號隔開在最後一個變量後面添加冒號和類型標註。

var red,green,blue:Double

注意:
在實際開發中咱們不多須要給常量/變量來寫類型標註,若是在定義常量/變量的時候給了一個初始值,那麼Swift能夠幫咱們推斷出常量/變量的類型。具體請參考類型和安全判斷。在上面定義的變量welcomeMessage中,咱們沒有給它賦初值,所以變量welcomeMessage的類型是從類型標註裏判斷的,而不是從它初始值判斷的。

常量和變量的命名

常量和變量的名字能夠包含任意字符串,固然也包括了Unicode
字符。

let π = 3.14159
let 你好 = "你好世界"
let 🐶🐂 = "dogcow"

常量和變量的命名不能包含空格字符,數學符號,箭頭,保留的(或者非法的)Unicode碼位,連線與製表符。也不可以用數字開頭,儘管數字可能在名稱的其餘地方可使用。

一旦你爲常量和變量聲明一個肯定的類型以後,你不可以再聲明一個相同名字類型的常量/變量,而且也不可以改變它所存儲的類型,固然你也不可以對常量和變量的互換操做。

注意:
若是你須要使用和Swift保留關鍵字相同的名稱做爲常量和變量名。你可使用關鍵字反引號(`)將關鍵字包起來做爲它的名字,不到萬不得已的時候,建議你不要使用關鍵字來做爲常量和變量的名字。

你能夠改變一個變量的值爲另一種相同類型的值,在下面的例子中,friendWelcome的值從hello!變爲Bonjour!

var friendWelcome = "hello !"
friendWelcome = "Bonjour !"
// friendWelcome  is now "Bonjour!"

和變量不一樣的是常量在初始化設置之後就不可以發生改變了。當你改變常量值的時候,編譯器會報錯。

let languageName = "Swift"
languageName = "c++"
//Cannot assign to value: 'languageName' is a 'let' constant
// 這裏是一個編譯錯誤,languageName 不可以被改變.

打印出常量和變量

你能夠用print(_:separator:terminator:) 這個函數打印出常量和變量的當前值。

print(friendWelcome)
// 打印出 friendWelcome 的值

 print(_:separator:terminator:)函數是一個在全局的指定區域打印出一個甚至更多值的的函數。例如在Xcode裏面,print(_:separator:terminator:)是在xcode的控制檯輸出的。separatorterminator參數都有默認值,所以你當你調用函數的時候能夠省略掉它們。函數默認經過換行符來結束當前行。若是不想換行,能夠默認添加一個空的字符串來代替,例如:print(someValue, terminator: "").若是想了解更多關於參數的信息,能夠查看默認參數

Swift用字符串插值的方式把常量名和變量名當作佔位符加入到長字符串中,Swift會把常量和變量的值替換掉這些佔位符。把常量和變量名放到括號裏面,並在括號前面加上反斜槓來轉義。

print("The currenct value of firendlyWelcome is \(friendWelcome)")
// 打印出當前firendlyWelcome的值

注意:
字符串插值全部能用的選項在這裏能找到。

註釋

把你代碼裏不用執行的文本用打上註釋做爲註解或者一個標記來提醒你本身。在swift中,編譯器會忽略掉註釋的編譯。

在swift中註釋和在c語言的註釋十分類似。單行的註釋用//來表示。

// This is a comment.

多行註釋以/*開始,以*/結尾。

/*
 This is also a comment.
 but is written over multiple lines. 
 */

和C語言中多行註釋不一樣的是,swift中多行註釋是能夠進行嵌套的。你能夠寫一個嵌套的多行註釋用/*開始 ,而後添加第二個多行註釋以/*開始以*/結束。最後用*/來結束第一個多行註釋。

/* This is the start of the first multiline comment.
 /* This is the second,nested multiline comment. */
 This is the end of the first mulitiline comment.*/

註釋的多行嵌套使得你在註釋大量代碼塊的時候更加便捷和容易,即便代碼裏已經存在多行註釋也沒有影響。

分號

和其餘編程語言不一樣的是,在swift中,在代碼的結尾,不須要你寫分號(;)固然,你也能夠按照你的習慣來添加分號。有一種狀況是必需要添加分號的,那就是在一行中執行多個語句

let cat = "🐱";print(cat)
// print "🐱"

整數

整數就是沒有小數的數字,好比說:42和-23.整數能夠是有符號的類型(正數,負數和0),也能夠是無符號的類型(正數和0)。

swift爲咱們提供了8位,16位,32位,以及64位的無符號和有符號的整數。這些整數的命名規則和c語言相似。好比說:有無符號的8位整數UInt8和有符號的32位整數Int32。像swift中的其餘類型同樣,整數類的命名都是大寫字母開頭。

整數的範圍

你能夠經過訪問整數的最大值屬性和最小值屬性在肯定他們的範圍。

let minValue = UInt8.min // UInt8的最小值爲0
let maxValue = UInt8.max // UInt8的最大值爲2
print("minValue of UInt8 is \(minValue) and maxValue of UInt8 is \(maxValue)。")

這些屬性的值代表了這種類型數據只能在規定範圍內進行操做(就像上面UInt8的例子),所以同種類型的數據能夠在表達式中一塊兒使用。

Int

在大多數狀況下,在寫代碼的過程當中咱們並不須要指定一個長度給IntegerSwift提供了另一個整數類型的數據Int,它的長度和原平生臺的字節數相同。

  1. 在32位的平臺上,Int的長度和Int32一致。
  2. 在64位的平臺上,Int的長度和Int64一致。

若是你不須要爲整型添加特殊的長度處理,用默認的Int來實現代碼就行。這能夠提升代碼的一致性和可複用性。甚至是在32位的平臺上,他可以儲存在-21474836482147483647範圍之間的數據,在不少時候這個範圍內的數據已經很大了。

UInt

swift也爲咱們提供了無符號類型的整型數據。UInt和原平生臺有着相同長度的字節數。

  1. 在32位的平臺上,UInt的長度和UInt32一致。
  2. 在64位的平臺上,UInt的長度和UInt64一致。

注意:
儘可能不要使用UInt,除非你真的須要存儲一個和當前原平生臺長度相同的字節數的無符號整數時候,若是不是這種狀況,建議你最好使用Int,即便你要存儲的對象已知是非負的。統一使用Int提升了代碼的一致性和可複用性。避免在不一樣的數據類型進行轉換,而且匹配數字的類型進行判斷,具體請參考類型安全和類型推斷

浮點型數據

所謂浮點型數據就是帶有小數部分的數據。好比:3.142580.1-273.15

浮點型所表明值得範圍要比整型要更大,它可以儲存比整型更小或者更大的值。Swift提供了兩種有符號的浮點數類型。

  1. Double類型表明的64位浮點型數據。
  2. Float類型表明的32位浮點型數據。

注意:
Double類型精度至少爲小數點後15位,Float類型的精確度僅僅是小數點後6位。
你能夠根據本身編程的須要值的範圍選擇是Double類型仍是Float類型,若是兩種條件都知足,優先選擇Double

類型安全和類型判斷

Swift是一個類型安全的語言,類型安全的語言可讓你清楚的知道你所處理的代碼值的類型。若是你代碼中須要的是String,那麼。你若是給它賦值爲Int類型的數據,那麼編譯器就會報錯。

由於Swift是類型安全的語言,因此它在編譯的時候會對代碼進行類型的檢查。這在開發過程當中可以幫助你儘量早的發現和解決問題。

當你在處理不一樣類型數據的時候,類型檢查可以幫助避免一些問題。然而,這並不意味着你在每次聲明常量或者變量的時候都須要顯示指定類型。若是你沒有指定顯示類型,那麼swift會使用類型判斷來爲你選擇合適的類型。類型判斷確保了編譯器在編譯代碼的時候經過檢查你賦的值自動推斷出表達式的類型。

由於有了類型判斷,和c或者c++相比來講,swift不多須要你進行類型聲明。常量和變量雖然須要來明確類型,可是大部分工做並不須要你來完成,編譯器已經爲你完成了。

當你聲明一個常量或者變量賦初值的時候類型判斷變很是有用。在你聲明常量或者變量的時候,賦給它們一個字面量(literal value或者literal)就可以讓編譯器本身來進行類型判斷。(所謂字面量就是直接出如今代碼中的值,好比下面例子中的423.14159。)

例如:若是你給一個新的常量用字面量的形式給它賦值爲42沒有聲明它的數據類型,Swift能推斷出你要給常量賦一個Int類型的數據,由於你在初始化的時候給它賦值了一個像整型的數字。

let meaningOfLife = 42
// meaningOfLife will be inferred to be of  type Int  這裏常量meaningOfLife 會被便以及推斷爲一個整型

同理,若是你沒有給浮點類型的數據標記類型,那麼Swift會默認類型的Double

let  pi = 3.14159
// pi will be inferred to be of type Double 這裏常量pi會被默認推斷爲DoubleL類型的數據。

當swift在推斷浮點型數據的時候,它會默認推斷爲Double類型而不是Float類型。

若是你在一個表達式中用整型和浮點型混合計算的時候,在上下文中會被swift推斷爲Double類型。

let anotherPi = 3 + 0.14159
// anotherPi will be inferred of type Double 這裏常量anotherPi會被swift推斷爲Double類型的數據。

初始值3沒有給顯式聲明類型,而且在表達式中出現了一個浮點類型的字面量,因此表達式被推斷爲Double類型。

數值型字面量

整數類型的字面量能夠被寫做:

  1. 一個十進制數,沒有前綴
  2. 一個二進制數,前綴是0b
  3. 一個八進制數,前綴是0o
  4. 一個十六進制數,前綴是0X

下面全部的整數型字面量都是表明十進制的值17

let decimalInteger = 17
let binaryInteger  = 0b10001// 二進制的17 2*2*2*2+1
let octalInteger = 0o21 // 八進制的17 2*8+1*1
let hexadecimalInteger = 0x11 // 十六進制的17 16*1+1

浮點型的字面量能夠是十進制(沒有前綴),也能夠是十六進制的(帶有前綴)。在小數的兩遍必須是十進制的值值(或者十六進制有前綴的值)。十進制的浮點數能夠有一個可選的指數(exponent)。一般用大寫的或者小寫的e來表示,十六進制的浮點數,一般用大寫或者小寫的p來表示。

若是一個十進制的指數爲exp,那麼這個數至關於基數和10^exp的乘積.
例如:
1.25e2表示的是1.25*10^2或者125.0
1.25e-2表示的是1.25*10^-2或者0.0125
若是一個有前綴的十六進制的指數爲exp,那麼這個數至關於基數和2^exp的乘積。
例如:
0xFp2表示的是15*2^2或者60.0
0xFp-2表示的是15*2^-2或者3.75
下面的浮點型的字面量都等價於十進制的值12.1875

//浮點類型的十進制的12.1875
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
//計算方式:0x表明16進制C表明12後面.3表明小數 因此整數部分應該是應該是12*(16^0)+0.3*(16^-1) 
後面的p表明指數,以2爲底。因此完整表達式爲(12*(16^0)+0.3*(16^-1))*(2^0)

數字類型的字面量添加額外的標記可以使它們看起來更容易閱讀。整數和浮點型均可以添加額外的零加上下劃線來加強可閱讀性,而且不會影響字面量的值。

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_000_1

數值類型轉換

即便常量和變量你已經知道它們是非負的,你也能夠在代碼中用Int類型來修飾它們。使用默認整數數據類型保證了常量和變量能夠被複用而且能用來匹配整數字面量的類型推斷。

當咱們作特殊的任務時候纔會對整型類型作類型指定,好比:須要處理的外部的長度明確了數據或者爲了性能優化、內存佔用等等。使用顯式指定長度的類型能夠及時的發現值的溢出而且還能標記咱們在處理的是特殊的數據。

整數類型轉換

不一樣類型的整數能夠保存不一樣範圍的常量和變量。Int8類型的整數能夠存儲數據的範圍是-128127。無符號整型的能夠存儲常量和變量的範圍是0255。若是數字超出了常量和變量對應值的範圍,編譯的時候會報錯。

let cannotBeNegative:UInt8 = -1 //不在UInt8值範圍內報錯
let tooBig:Int8 = Int8.max + 1  //超出Int8值範圍內報錯

由於不一樣數據類型可以存儲數據的範圍是不同的,在進行類型轉換的時候你必須選擇一個合適的數據類型進行轉換。這種轉換方式可以讓你的代碼的意圖更明顯,而且可以防止你在隱式轉換中遇到的錯誤。

在轉換一個特殊的數字類型到另外一個類型的時候,你須要從新初始化一個你須要的目標數據。在下面的例子中,常量twoThousand是一個UInt16類型的數據,和它一塊兒須要轉換的數據類型是UInt8,它們兩個不可以直接相加,由於他們兩個類型不同。
咱們能夠用UInt16(one)來建立一個UInt類型的數據用one的值進行初始化。用初始化的數據類代替原始數據。

let twoThousand:UInt16 = 2_000
let one:UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)

由於等式兩邊的類型都是UInt16,因此二者等式操做是被容許的。輸出的常量(twoThousandAndOne)的數據類型被swift推斷爲UInt16,由於它是兩個UInt16的常量相加而來的。

SomeType(ofInitialValue)調用的是swift構造器並傳入一個初始值的默認方法。在語言的內部,UInt16有一個構造器能夠接收一個UInt8類型的數據。這個構造的做用是經過已經存在的UInt8類型的數據初始化一個UInt16類型的數據。須要注意的是:你並不能任意的傳入值,只有在傳入UInt16內部有對應構造器的值。不過你能夠擴展示有的類型,讓它來接收其餘類型的值(包括自定義的類型),具體請參考擴展

整數和浮點數轉換

在進行整數和浮點數類型轉換的時候必定要指定類型。

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi等於3.14159,被Swift推斷爲double類型

在這裏,常量three被當作一個Double類型的數據來建立,所以+兩邊應爲相同的類型。若是這裏沒有進行轉化,那麼+(加號運算符)是不被容許使用的。
浮點型的數據和整型同樣能夠互相轉換。整型可使用DoubleFloat進行初始化。

let integerPi = Int(pi)
//這裏integerPi等於3,被Swift推斷爲Int類型。

上面咱們使用浮點型數據進行初始化一個整型數據的時候,咱們發現浮點值被截斷了。這意味着4.75變成了4,-3.9被截斷爲-3。
注意:
結合數字型的常量和變量的規則和數字字面量的規則是不一樣的,在字面量3能夠和直接和字面量0.14159相加,由於數字型的字面量他們自己值是沒有特定類型的。它們的類型只有在編譯須要計算值的時候纔會被推斷。

類型別名

類型別名是給已經存在的一個數據類型添加一個可選的名字。你能夠用關鍵字typealias來定義這是一個類型別名。

當你想給現有的類型添加一個有意義的名字的時候,類型別名顯得特別的有用。咱們假設你正在處理特定長度的外部資源的數據的時候:

typealias AudioSample = UInt16

當你定義過一個類型別名的時候,你能夠在任意一個地方像使用原始名同樣來使用這個別名。

var maxAmplitudeFound = AudioSample.min
//  maxAmplitudeFound 如今的值是0

在這裏,AudioSample被定義成一個值爲UInt16的別名。正由於這是一個別名,因此咱們調用AudioSample.min實際上就是調用UInt16.min這個函數,這樣爲變量maxAmplitudeFound提供了一個初始值0

布爾值

Swift 有一個基礎的布爾類型叫作Bool,布爾的值是邏輯上的值,所以只有真和假。Swift爲咱們提供了兩個bool常量truefalse

let  ornagesAndOrange = true
let  turnipsAndDelicious = false

ornagesAndOrangeturnipsAndDelicious被推斷爲Bool由於他們是由Bool字面量初始化來的。就像上面提到的IntDouble類型,當在聲明的時候你只須要設置一下他們的值true false不須要聲明常量或者變量的類型。當初始化常量或者變量的時候若是要賦值的類型是已知的,就能夠觸發便以及的類型推斷,這使得Swift的代碼更加簡潔和易讀。

當你在寫條件語句好比if語句的時候,布爾值顯得更加有用。

if turnipsAndDelicious {
    print("Mmm, tasty turnips!")
} else {
    print("Eww, turnips are horrible.")
}
// 這裏打印的是:Eww, turnips are horrible.

關於條件語句,好比if語句,能夠參考控制流
若是你在使用Bool值的時候賦了其餘類型的值,那麼swift由於類型安全就會報錯。下面的例子中就會報編譯的錯誤。

let i= 1
if i {
// 這個例子不會編譯成功,會報錯(報錯緣由是 判斷的值不是bool類型)
}

然而,下面的例子是合法的:

let i = 1
if i == 1 {
    // 這個例子能編譯成功不報錯。由於這裏i == 1 是一個判斷語句
}

由於等式i == 1的結果是一個Bool類型的值,因此第二個例子可以經過類型檢查。像i == 1這樣類型的比較,參考 基本操做符
和swift中其餘類型安全的例子同樣,這個方法避免了一些偶然的錯誤而且保證了代碼的目的老是清晰的。

元組

元組(Truples)就是把多個值組合成一個複合值。元組中的成員並不必定是相同類型的數據,它們能夠是任意類型的數據。
在這個例子中(404,"Not Found")就是一個表明HTTP狀態碼的元組。HTTP狀態碼是當你請求網頁的時候,web服務器返回的一個特殊值。若是請求的網頁不存在就會返回狀態碼404 NOT Found

let http404Error = (404,"Not Found")
// http404Error的類型是(Int,String)的元組,值是(404, "Not Found")

(404,"Not Found") 這個元組把IntString的值放到一塊兒組合起來表示HTTP請求的狀態碼的兩部分:一個數組和另一個可讀的描述。它被描述爲一個類型爲(Int,String)的元組。

你能夠把任意順序的類型組合爲一個元組,這個元組能夠你須要的任意類型的數據。你能夠建立一個類型爲(Int,Int,Int)或者(String,Bool)或者其餘的任何你想建立任意組合的元組。

你能夠將元組的內容分解爲單獨的常量或者變量,而後你就能夠正常使用它們了。

let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
//輸出的狀態碼爲 404
print("The status message is \(statusMessage)")
// 輸出的狀態描述爲 Not Found

若是你只須要元組中的其中一個值,那麼分解的時候你能夠用_將不須要的部分省略掉。

let  (justTheStatusCode,_) =  http404Error
print("The status code is \(justTheStatusCode)")
// 輸出的狀態碼爲 404

此外,你還能夠用下標來訪問元組中的某個值,元組中的下標從0開始。

print("The status code is \(http404Error.0)")
//輸出的狀態碼爲 404
print("The status message is \(http404Error.1)")
// 輸出的狀態描述爲 Not Found

你能夠在定義元組的時候給單個元組進行命名

let http200Status = (statusCode:200,description:"OK")

當元組中的元素命名後,你能夠經過名字來獲取對應的元素的值。

print("The status code is \(http200Status.statusCode)")
////輸出的狀態碼爲 200
print("The status message is \(http200Status.description)")
// 輸出的狀態描述爲 OK

元組在用做函數返回值的時候顯得尤其重要。已給獲取網頁請求狀態地方函數能夠會返回一個(Int,String)元組來描述網絡請求的狀態。這和只能返回一個值進行比較來講。一個包含兩個不一樣類型值的元組返回地方信息更有用。瞭解更多請參考函數與返回值

注意:

元組在組織臨時值的時候頗有用,它們並不適合用到複雜的數據結構裏。若是你的數據結構不是臨時的使用,那麼請使用類或者結構體而不是用元組。瞭解更多,請參考類與結構體

可選類型

當處理值缺失狀況的時候,你能夠用可選類型來表示。可選類型代表有兩種可能性:或者有值,你能夠解析這個可選類型來訪問這個值。或者這個值不存在。
注意:
C語言和Objective-C語言中不存在可選類型這個概念。在Objective-C語言中最接近的是一個方法中不要麼返回一個對象,要麼返回nilnil表示缺乏一個合法的對象。然而,這隻對對象起做用,對於結構體,基本的C數據類型以及枚舉值都不起做用。對於這些類型,Objective-C方法中通常會返回一個特殊值(好比NSNotFound)來暗示值的缺失。這種方法假設方法的調用者知道並對這些特殊值進行判斷。然而,Swift的可選類型讓你明白任意類型額值缺失,並不須要一個特殊的常量。

這裏有一個關於可選值是怎麼被用做當作缺省值的例子,Swift中整型中有一個構造器,做用是將一個String類型的值轉換成一個Int類型的值。然而,並非全部的字符串都可以轉化爲整型。字符串123能夠被轉化爲整型123,可是字符串Hello,world不能被轉化爲整型。

下面的例子中使用這種便利構造器將String轉換成Int類型。

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
//這裏的convertedNumber 被編譯器推斷爲'Int'類型,或者類型 ‘optional Int’

由於這個構造器也可能失敗,因此它的返回了一個可選類型的(optional)Int而不是一個整型。一個可選的Int類型寫做Int?而不是Int。這裏的問號包含的值是可選類型,也就是說可能包含Int;類型的值也可能不包含。(不能包含其餘的類型,好比說Bool類型的值或者String類型的值,它只能是Int類型,或者不存在這個類型。)

nil

你能夠給一個不存在的可選類型的變量賦值爲nil

var serverResponseCode:Int? = 404
// serverResponseCode中包含了一個可選的Int類型的值404
serverResponseCode = nil
// 這裏serverResponseCode的值爲nil(值不存在)

注意:
你不可以把nil用在非可選的變量和常量上。若是你的代碼中存在常量或者變量值缺省的狀況,那麼在聲明的時候就聲明爲可選變量或者常量。

若是你不提供可選類型的變量的初始值,那麼變量會自動設置爲nil

var surveyAnswer:String?
//這裏surveyAnswer將會被自動設置爲nil

注意:
Swift中的nilObjective-C中的nil意義不同。在Objective-C中,nil是一個指向不存在對象的一個指針。在Swift中,nil不是一個指針,它是一個肯定類型的值。任何類型的可選狀態均可以被設置爲nil,不只僅是對象類型的數據。

if語句以及強制解析

你可使用if語句和nil來判斷比較已給可選值是否包含值。你可使用(==)以及(!=)等於以及不等於兩個操做符來判斷一個可選值是否包含值。

若是一個可選類型是有值的,那麼它被認爲不等於nil

if convertedNumber != nil {
    print("convertedNumber contains some integer value.")
}
// 輸出convertedNumber contains some integer value.

當你肯定這個可選值確實包含值的時候,你能夠在可選的名字後面加一個!來獲取值。
這個感嘆號表示我知道這個值有可選值,請使用它。這種被稱爲可選值的強制類型解析。

if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber).")
}
//輸出的是 convertedNumber has an integer value of 123.

更多關於if條件語句的介紹,請參考控制流

注意:
使用來獲取一個不存在的值可能會致使運行時的錯誤。在進行強制類型解析!的時候要注意,確保可選類型必定要包含一個不爲nil的值。

可選綁定

使用可選綁定來找出一個可選類型是否包含值,若是包含,若是包含就把值賦給一個臨時變量或者常量。可選綁定能夠用在if和while等條件語句中,這條語句不只僅能夠判斷可選類型中是否有值,同時也能夠將可選類型中的值賦給一個常量或者變量。關於if和while 語句,。請參考控制流

像下面同樣用if語句寫一個可選綁定:

if let constantName = someOptional {
    statements
}

你能夠像上面那樣使用可選綁定來重寫在例子可選類型中列舉的possibleNumber例子。

if let actualNumber = 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包含的一個值,建立一個新的叫作actualNumber的常量並將包含的值賦給它。

若是轉換成功,那麼actualNumber常量能夠在if語句的第一個分支中使用。它已經被可選類型包含的值初始化過,這裏就不須要再變量的後面添加!來進行強制解析獲取它的值了。在這個例子中,actualNumber只是被用來輸出轉換的結果。

常量和變量均可以使用可選類型綁定。若是你想在if的第一個分支語句中使用actualNumber,那麼你能夠把判斷條件改成if var actualNumber 這裏就把可選類型中包含的值就被賦值給一個變量而不是一個常量。
你能夠在if語句中使用多個可選綁定或者Bool類型來做爲條件判斷,它們以前用逗號隔開。若是可選類型中任意一個值爲nil或者Bool類型中判斷結果爲false,那麼整個if語句的條件判斷就會被認爲是false。下面例子中的if語句是等價的:

if let firstNumber = Int("4"),let secondNumber = Int("42"),firstNumber < secondNumber && secondNumber < 100 {
    print("\(firstNumber) < \(secondNumber) < 100")
}
// 打印的結果是:4 < 42 < 100
if let firstnumber = Int("4") {
    if  let  secondnumber = Int("42") {
        if firstnumber < secondnumber && secondnumber < 100 {
            print("\(firstnumber) < \(secondnumber) < 100")
        }
    }
}
// 打印的結果是:4 < 42 < 100

注意:
在if語句中建立的常量和變量的可選綁定,只能在body中使用。相反,在guard語句中建立的常量和變量的可選綁定,只有在guard語句以外可以取到值,請參參考提早退出

隱式解析可選類型

就像上面描述的同樣,可選類型暗示了常量和變量能夠沒有值,可選類型經過if語句來判斷是否有值,若是有值的話能夠經過可選綁定來進行值的解析。

有時候在程序的結構中,在第一次被賦值之後,能夠肯定一個可選的類型老是有值。在這種狀況下,每次都進行判斷和解析可選值是很低效的,由於能夠肯定這個值是老是存在的。
這種類型的可選狀態被定義爲隱式解析可選類型implicitly unwrapped optionals。把可選類型(String?)後面的問號改成(String!)歎號來聲明一個隱式解析可選類型。

當可選類型在第一次被賦值之後就能夠肯定一直有值的時候,隱式解析可選類型顯得頗有用。隱式解析可選類型主要被用在Swift中類的構造器中,請參考無主引用以及隱式解析可選屬性

一個隱式解析可選類型實際上就是一個普通的可選類型,可是能夠被當作非可選類型來使用,並不須要每次都是用解析來獲取可選值。下面的例子中展現了可選類型String和隱式解析可選類型String行爲之間的區別。

let  possibleString:String? = "An optional string."
let forcedString:String = possibleString!
//須要用感嘆號來獲取值
let  assumedString:String! = "An implicitly unwrapped optional string."
let implicitString:String = assumedString
// 不須要用感嘆號就能獲取值

你能夠把隱式解析可選類型當作一個能夠自動解析的可選類型。你要作的就是在聲明的時候把感嘆號放到類型的結尾,而不是放到每次取值的可選名字的結尾。

注意:
若是你在隱式可選類型中沒有值的時候嘗試取值,那麼會觸發運行時的錯誤。這和你在沒有值的普通可選類型後面添加一個歎號同樣。

你仍然能夠把隱式解析可選類型當作普通的可選類型來判斷它是否包含值:

if assumedString != nil {
    print(assumedString!)
}
// 打印出 An implicitly unwrapped optional string.

你能夠在單個語句的可選綁定類型中使用隱式解析可選類型來檢查分析它的值:

if let definiteString = assumedString {
    print(definiteString)
}
// 打印出 An implicitly unwrapped optional string.

注意:

若是一個變量在以後可能會是nil的時候,不要使用隱式解析可選類型。若是你須要在變量的聲明週期中判斷是否爲nil的時候,使用普通可選類型。

錯誤處理

你可使用錯誤處理(error handling)來應對程序運行過程當中可能會出現的錯誤狀況。

與可選值不同的是,運用值的存在與缺失來表達函數執行的成功與失敗,錯誤處理能夠推斷出失敗的緣由。並能夠傳播到程序的其餘地方。

當一個函數遇到出現錯誤條件的時候,它能拋出錯誤。調用函數方法的地方能拋出異常併合理的響應。

func canThrowAnError () throws {
    // this function may or may not throw an error
}

一個函數在聲明中添加一個throws關鍵字是的時候來拋出錯誤消息。當一個函數可能會拋出異常的時候,你應該在表達式中使用前置try關鍵詞。

Swift會自動將錯誤傳播到當前的範圍直到它們被catch句子來處理。

do {
    try canThrowAnError()
    // no error was thrown
} catch  {
    // an error was thrown
}

一個do語句建立了一個新的做用域,它容許錯誤被傳遞到一個或者多個catch語句中。
這是一個如何運用錯誤處理來應對不一樣錯誤狀況的例子:

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleardishes {
    washDishes()
} catch SandwichError.missingTngredients(let ingredients) {
    buyGroceries(ingredients)
}

在這個例子中,makeASandwich()函數會拋出一個錯誤消息條件是若是沒有乾淨的盤子或者某個原料的缺失。因爲makeASandwich()可能會拋出錯誤,因此函數調用被包在了try語句中。將函數包在一個do語句中,那麼任何被拋出的錯誤都會被傳播到catch語句中。
若是沒有錯誤被拋出,那麼eatASandwich()函數會被調用。若是一個匹配SandwichError.outOfCleardishes的錯誤被拋出,那麼會執行washDishes()這個函數,若是異常SandwichError.missingTngredients被捕獲到,那麼buyGroceries(_:)函數將會被調用,而且會使用catch所捕獲到的關聯值String做爲參數。
拋出,捕獲以及傳播錯誤等,會在錯誤處理的章節中說明。

斷言和先決條件

斷言和先決條件是在運行時進行檢查的。你能夠用它們來檢查在執行後面代碼的以前是否一個必要的條件已經知足了。若是斷言或者先決條件中的bool值是true的時候,那麼代碼會像往常同樣執行。若是條件判斷爲false,當前程序的狀態是無效的,代碼的執行會結束,你的app會被終止。

你使用斷言和先決條件來表達你作的假設和你在編碼時候的但願執行的方式。你能夠將這些包含在代碼中。斷言幫助你在開發階段找到錯誤和不正確的假設,先決條件幫助你在在生產環境中發現存在的問題。

除了在運行時驗證你的指望值,斷言和先決條件也變成了你代碼中一種有用的文檔形式。和上面討論的錯誤處理不一樣,斷言和先決條件不是用來處理能夠恢復或者能夠預期的錯誤。由於一個斷言失敗代表了程序正在處於一個無效的狀態,沒有辦法來捕獲一個失敗的斷言。

使用斷言和先決條件不是一個可以避免出現程序無效狀態的編碼方法。然而,若是一個無效狀態的程序產生快了。斷言能夠強制檢查你的數據和程序狀態,使程序按照預測中的被終止,並幫助咱們更簡單的對這個問題進行調試。一旦遇到無效的狀態,程序就會被終止,防止無效的狀態對程序形成進一步的傷害。

斷言和先決條件的不一樣之處在於它們何時進行檢測。斷言僅僅在調試的時候運行,可是先決條件不只僅在調試的時候能運行在生產環境下也能運行。在生產環境下,斷言條件不會被評估。這意味着你能夠在開發階段多使用斷言,這些斷言在生產條件下不會形成影響。

使用斷言進行調試

你能夠用Swift標準庫的函數assert(_:_:file:line:)來編寫一個斷言。
你向這個函數傳入一個判斷結果爲true和false的表達式以及一條錯誤狀況下展現的信息。例如:

let age = -3
assert(age >= 0,"A person's age can't be less than zero.")
// 這個斷言會失敗 由於一我的的年齡不可能小於0(把斷言中的語句變爲age <= 0,就不會走這個斷言)

在這個例子中,只有age >= 0true也就是說age的值爲非負數的時候,代碼纔會繼續執行。若是age的值爲負數,就像上面代碼中的同樣,那麼,age >= 0false,斷言失敗,使得應用被終止運行。

你也能夠省略掉斷言的提示信息,例如:當斷言條件可能會重複執行的時候

assert(age >= 0)

若是代碼已經被檢查過,你可使用函數assertionFailure(_:file:line:)來代表斷言失敗了。例如:

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.")
}

強制執行先決條件

當一個條件可能爲假,可是繼續執行下去要求條件必須爲真的時候,須要使用先決條件。例如:使用先決條件來判斷下標有沒有越界,或者來檢查是否將一個正確的參數傳給了函數。

你可使用函數precondition(_:_:file:line:)來寫一個先決條件。向這個函數傳入一個結構爲true或者false的表達式以及一條錯誤條件下顯示信息。例如:

// 下標的實現
precondition(index > 0, "Index must be greater than zero.")

你能夠調用函數preconditionFailure(_:file:line:)來代表出現了一個錯誤,例如:switch的進入了default分支,全部的有效輸入數據都應該被其餘分支所處理而不是默認的default分支。
注意:
若是你使用不檢查的模式(-Ounchecked)進行編譯,先決條件將不會進行檢查。編譯器會假設全部的先決條件都是true,這將優化你的代碼。然而致命的錯誤函數(_:file:line:)老是中斷執行,不管你進行什麼樣的優化設置。

在設計原型和早期的開發階段你可使用致命的錯誤函數(_:file:line:),這個階段只是對方法的聲明,
可是沒有具體的實現。你能夠在方法fatalError("Unimplemented")進行具體實現。由於fatalError不會像斷言和先決條件那樣能夠被優化,因此你能夠確保當代碼執行到一個沒有實現的方法是的時候,程序會被中斷。

更多swift4.1翻譯請查看github

QQ技術交流羣:214541576

微信公衆號:shavekevin

開發者頭條:

熱愛生活,分享快樂。好記性不如爛筆頭。多寫,多記,多實踐,多思考。

相關文章
相關標籤/搜索