類型安全和類型推斷(Type Safety and Type Inference)
- Swift是一個(gè)類型安全的語言,所以會在編譯的時(shí)候會檢查類型不匹配的錯(cuò)誤。這么做可以盡可能早的在開發(fā)過程中發(fā)現(xiàn)錯(cuò)誤。
- 類型檢查幫助避免在不同類型的值的錯(cuò)誤。雖然這樣,但也不意味著必須把每個(gè)變量(常量)的類型寫出來。因?yàn)镾wift使用類型推斷來給出合適的類型。
let meaningOfLife = 42
//meaningOfLife is inferred to be of type Int
當(dāng)不聲明一個(gè)浮點(diǎn)型時(shí),默認(rèn)是Double類型。
let pi = 3.14159
//pi is inferred to be of type Double
let anotherPi = 3 + 0.14159
//anotherPi is also inferred to be of type Double
數(shù)字型的字面值(Numeric Literals)
- 整型的字面可以這么寫:
- 十進(jìn)制:不需要前綴
- 二進(jìn)制:前綴為"0b"
- 八進(jìn)制:前綴為"0o"
- 十六進(jìn)制:前綴為"0x"
以下這些的值都是17
let decimalInteger = 17
let binaryInteger = 0b10001
let octalInteger = 0o21
let hexadecimalInteger = 0x11
-
浮點(diǎn)型的字面可以為十進(jìn)制(沒有前綴),也可以為十六進(jìn)制(前綴為"0x")。十進(jìn)制數(shù)用大寫或者小寫的e表示指數(shù),十六進(jìn)制數(shù)用大寫或小寫的p表示指數(shù)。
十進(jìn)制:- 1.25e2 means 1.25 x 10^2, or 125.0.
- 1.25e-2 means 1.25 x 10^(-2), or 0.0125.
十六進(jìn)制:
- 0xFp2 means 15 x 2^2, or 60.0.
- 0xFp-2 means 15 x 2^(-2), or 3.75.
下面這些浮點(diǎn)型的值都是12.1875
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
numeric literals為了更好的閱讀代碼可以包含額外的格式,整型和浮點(diǎn)型都可以使用前綴"0"和下劃線"_"
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
數(shù)值類型的轉(zhuǎn)換(Numeric Type Conversion)#
- 整型的轉(zhuǎn)換(Integer Conversion)
當(dāng)然,整形的存儲是不能超過類型的范圍的,那UInt8(0255)和Int8(-128127)為例:
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
當(dāng)兩種數(shù)值不是一個(gè)類型的時(shí)候是不能進(jìn)行運(yùn)算的,哪怕他們都是“整型”:
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let towThousandAndOne = twoThousand + UInt16(one)
//swift的強(qiáng)制轉(zhuǎn)換和OC是不同的,swift是將類型放在括號外面,而將變量(常量)放在括號里面。
- 整型和浮點(diǎn)型之間的相互轉(zhuǎn)換(Integer and Floating-Point Conversion)
在整型和浮點(diǎn)型之間轉(zhuǎn)換一定要明確:
let three = 3
let pointOneFourOneFiveNine = 0.141459
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double
類型的別稱(Type Aliases)
類型的重命名用到的是"typealias"關(guān)鍵字。
typealias AudioSample = UInt16
//correct
let max: AudioSample = AudioSample.max
//wrong
let max: AudioSample = UInt16.max
布爾型(Boolean)
Swift中的布爾型與C語言和OC都不一樣,為"Bool",而其值和C語言是一直的,分別為"true"和"false"。
因?yàn)閠ype safety的緣故,下面的代碼是錯(cuò)誤的
let i = 1
if i {
//this example will not compile, and will repot an error
}
所以,這段代碼應(yīng)該這么寫:
let i = 1
if i == 1 {
//this example will compile successfully
}
因?yàn)?i == 1"的返回值是Bool,所以這段代碼可以執(zhí)行。
元組(Tuples)
元組就是一組多個(gè)值合成為一個(gè)單一的值,而這一組的多個(gè)值并不是一定要相同的類型。
??:
let http404Error = (404, "Not Found")
//http404Error is of type (Int, String)
你可以在創(chuàng)建一個(gè)元組的時(shí)候任意的排序類型。
你也可以在定義一個(gè)元組的時(shí)候,將類型分離,??:
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
//Prints "The status code is 404"
如果你只需要為元組中的某個(gè)元素命名是,其他的可以用下劃線(_)代替:
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"
你也可以通過訪問元組的索引值來訪問相應(yīng)的元素:
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"
在定義一個(gè)元組的時(shí)候,就可以為每個(gè)元素命名,這樣就可以直接通過元素名稱來訪問:
let http200Status = (statusCode: 200, description: "OK")
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"
可選變量(Optionals)#
在C語言和OC中是沒有optionals的概念的,但在OC中有一個(gè)類似的類型就是NSNil。Optionals通俗點(diǎn)講,就是這個(gè)值可能存在,也可能不存在。舉個(gè)??:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"
- nil
在聲明一個(gè)變量的時(shí)候可以分配一個(gè)特殊的值——nil:
var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value
在聲明一個(gè)可選變量的時(shí)候不賦給初始值,默認(rèn)為nil(數(shù)值類型的值同樣為nil)。
- 可選綁定(Optional Binding)
可選綁定,如果一個(gè)可選變量的值不為nil,則將值賦給另外一個(gè)變量(常量)。可選綁定可以用在if和while的判斷條件中。??:
if let actualNumber = Int(possibleNumber) {
print("\"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
print("\"\(possibleNumber)\" could not be converted to an integer")
}
// Prints ""123" has an integer value of 123"
你可以在一個(gè)if判斷條件中包含多個(gè)可選綁定和"where"子句來進(jìn)行判斷,如果其中有任意一個(gè)可選綁定為空(nil),或者"where"的返回值為"false",整個(gè)可選綁定都會不成功:
if let firstNumber = Int("4"), secondNumber = Int("42") where irstNumber < secondNumber {
print("\(firstNumber) < \(secondNumber)")
}
// Prints "4 < 42"
- 隱式解析可選類型(Implicitly Unwrapped Optional)(也不知道是不是叫這個(gè)名字)
有些變量在聲明的時(shí)候就已經(jīng)有值了,如果還是每次都判斷是否為空就會非常低效,所以就有了這個(gè)Implicity Unwrapped Optional類型,意思就是我聲明的這個(gè)變量不為空,用感嘆號表示(!)。
下面這個(gè)??展示了Optionals和Implicity Unwrapped Optional的區(qū)別:
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation mark
同樣的,你可以判斷隱式解析可選類型是否為空(nil),也可以進(jìn)行可選綁定。
??:
if assumedString != nil {
print(assumedString)
}
// Prints "An implicitly unwrapped optional string."
if let definiteString = assumedString {
print(definiteString)
}
// Prints "An implicitly unwrapped optional string."
異常處理(Error Handling)
當(dāng)一個(gè)方法遇到錯(cuò)誤情況,它就會拋出(throws)一個(gè)錯(cuò)誤。而方法的調(diào)用者就會捕捉(catch)到錯(cuò)誤信息,并及時(shí)處理。
func canThrowAnError() throws {
//this function may or may not throw an error
}
當(dāng)一個(gè)方法可以拋出錯(cuò)誤的時(shí)候在它聲明方法名的時(shí)候要包括"throws"關(guān)鍵字。當(dāng)你調(diào)用一個(gè)可以拋出錯(cuò)誤的方法時(shí),在前面用"try"關(guān)鍵字來修飾。
Swift會自動(dòng)將錯(cuò)誤信息從當(dāng)先這個(gè)范圍傳遞出去,知道會遇到"catch"子句的時(shí)候才會進(jìn)行處理。
do {
try canThrowAnError()
//no error was thrown
} catch {
//an error was thrown
}
"do"狀態(tài)創(chuàng)建了一個(gè)新的可以允許錯(cuò)誤信息傳遞給一個(gè)或者多個(gè)"catch"子句的包含范圍。
??:
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
上面這個(gè)??中,"makeASandewich()"將會在沒有干凈盤子的時(shí)候執(zhí)行"washDishes()"。之所以"makeASandwich()"可以拋出一個(gè)錯(cuò)誤,是因?yàn)檫@個(gè)方法包含了"try"表達(dá)式。如果沒有錯(cuò)誤拋出,那么"eatASandwich()"這個(gè)方法就會執(zhí)行。
-
斷言(Assertions)
在某些情況下,如果一個(gè)特定的條件不滿足,那么代碼就不會繼續(xù)執(zhí)行。在這些情況下,你可以在你的代碼中設(shè)置一個(gè)斷言(asserttion)來結(jié)束代碼的執(zhí)行或者提供一個(gè)調(diào)試值為空或者不存在的機(jī)會。- 用斷言進(jìn)行調(diào)試(Debug with Assertions)
assertion會在runtime檢查一個(gè)Boolean表達(dá)式的值是否為true。字面的意思就是,一個(gè)assertion"斷言"一個(gè)表達(dá)式是否為真。如果表達(dá)式的值為true,那么代碼會照常執(zhí)行,如果表達(dá)式的值為false,代碼執(zhí)行結(jié)束,app會直接終止。
但你在調(diào)試情況下使用assertions,可以附加一條自己的信息。在Swift標(biāo)準(zhǔn)庫中,調(diào)用"assert(::file:line:)"方法,這是一個(gè)全球變量:
- 用斷言進(jìn)行調(diào)試(Debug with Assertions)
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
assertions的信息也可以省略。
assert(age >= 0)
- 什么時(shí)候使用斷言(When to Use Assertions)
使用assertions首先要確保表達(dá)是的值有可能為false,但是想要代碼繼續(xù)執(zhí)行的話,值一定為true。以下幾種情況適用assertions:- 一個(gè)整數(shù)下標(biāo)索引值傳到了一個(gè)自定義的下標(biāo)實(shí)現(xiàn),而值超出了范圍(太小或太大)。
- 給函數(shù)傳入一個(gè)值,但是非法的值可能導(dǎo)致函數(shù)不能正常執(zhí)行。
- 一個(gè)可選值現(xiàn)在為nil,但是代碼需要一個(gè)不為nil的值才能成功執(zhí)行。