基礎(chǔ)知識 (The Basics)
自從蘋果2014年發(fā)布Swift,到現(xiàn)在已經(jīng)兩年多了,而Swift也來到了3.1版本。去年利用工作之余,共花了兩個(gè)多月的時(shí)間把官方的Swift編程指南看完?,F(xiàn)在整理一下筆記,回顧一下以前的知識。有需要的同學(xué)可以去看官方文檔>>。
常量和變量 (Constants and Variables)
聲明常量和變量 (Declaring Constants and Variables)
使用let來聲明常量,用var來聲明變量。例如下面的常量和變量用來跟蹤用戶的輸入的密碼次數(shù):
let maximumNumberOfLoginAttemps = 10
var currentLoginAttemps = 0
當(dāng)然,我們還可以用一行代碼中聲明多個(gè)常量或者變量,并且用逗號隔開:
var x = 0.0, y = 0.0, z = 0.0
注意:如果在代碼中,你存儲的一個(gè)值永遠(yuǎn)不會變,請一定使用let來聲明;后續(xù)需要改變的值用var聲明。
類型注釋 (Type Annotations)
當(dāng)我們定義一個(gè)常量或者變量的時(shí)候,可以給一個(gè)具體的類型。例如:
var welcomeMessage: String
welcomeMessage = "Hello"
同樣,我們可以用一行代碼注釋多個(gè)變量的類型:
var red, green, blue: Double
在大多數(shù)情況下,我們不必注釋變量的類型,因?yàn)镾wift能根據(jù)常量或者變量的值來推斷類型。
命名常量和變量 (Naming Constants and Variables)
常量和變量名幾乎可以包含任意字符,包括Unicode:
let π = 3.14159
let 你好 = "你好世界"
let ???? = "dogcow"
常量和變量的命名規(guī)則和OC一樣,不能包括空格、數(shù)學(xué)符號、箭頭、私有(或非法)的Unicode代碼、線條和方塊圖字符;也不能以數(shù)字開頭,但數(shù)字可以出現(xiàn)在后面的其他位置。
打印常量和變量 (Printing Constants and Variables)
直接使用print(_:separator:terminator:)方法來打印:
print(welcomeMessage)
// Prints "Hello"
print(_:separator:terminator:)方法是一個(gè)全局方法,separator 和 terminator參數(shù)有默認(rèn)值,所以在使用這個(gè)方法時(shí)可以省略。
當(dāng)我們要拼接一個(gè)變量到字符串,并且打印出來,可以這樣寫:
print("\(welcomeMessage), Lebron James!")
// Prints "Hello, Lebron James!"
注釋 (Comments)
單行注釋
// This is a comment.
多行注釋
/* This is also a comment
but is written over multiple lines. */
內(nèi)嵌多行注釋
/* 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. */
分號 (Semicolons)
不同于其他語言,Swift在每行代碼后面不需要加分號。
整數(shù) (Integers)
Swift提供了8、16、32和64位的整數(shù)。這些整數(shù)的命名和C語言類似,例如8位無符號整數(shù)是UInt8,32位有符號整數(shù)是Int32。向其他Swift類型一樣,這些整數(shù)類型的名稱都是大寫字母開頭的。
整數(shù)邊界 (Integer Bounds)
我們可以使用min和max屬性來訪問各個(gè)整數(shù)類型的最小值和最大值。
let minValue = UInt8.min // UInt8的最小值是0
let maxValue = UInt8.max // UInt8的最大值是255
Int
在多數(shù)情況下,我們不必在代碼中指定整數(shù)的取值范圍。Swift提供了另外一個(gè)整數(shù)類型Int,它的取值范圍取決于當(dāng)前運(yùn)行平臺:
- 在32-bit的平臺上,
Int相當(dāng)于Int32 - 在64-bit的平臺上,
Int相當(dāng)于Int64
注意:當(dāng)你真的特別需要無符號的整數(shù)類型時(shí)才使用Unit,否則一般情況下是推薦使用Int,即使你知道你要存儲的值是非負(fù)數(shù)。一致使用Int能增強(qiáng)代碼的互動(dòng)性,可以避免不同數(shù)字類型的轉(zhuǎn)換。
UInt
Swift也提供了不帶符號的整型,UInt,他的取值范圍也取決于當(dāng)前運(yùn)行平臺:
- 在32-bit的平臺上,
UInt相當(dāng)于UInt32 - 在64-bit的平臺上,
UInt相當(dāng)于UInt64
浮點(diǎn)型 (Floating-Point Numbers)
浮點(diǎn)型的變量存儲的值范圍比整數(shù)型更廣。Swift提供了兩種有符號的浮點(diǎn)型類型:
-
Double:代表64-bit浮點(diǎn)型 -
Float:代表32-bit浮點(diǎn)型
注意:Double的精度可以達(dá)到15為小數(shù),而Float只能達(dá)到6位小數(shù)。選用哪一種類型取決于你的實(shí)際情況。一般情況下,我們直接使用Double。
類型安全與類型推斷 (Type Safety and Type Inference)
細(xì)心的同學(xué)可以發(fā)現(xiàn),在上面定義的一些變量,我們無需在最前面寫變量的類型,而是直接用let或者var。因?yàn)镾wift可以直接根據(jù)你賦給那個(gè)變量的值來推斷變量的類型。例如:
let meaningOfLife = 42 // meaningOfLife被推斷為Int類型,很明顯,我們從42字面上就能看出是一個(gè)整數(shù)
let pi = 3.14159 // pi被推斷為Double類型
在推斷浮點(diǎn)型數(shù)字的時(shí)候,Swift會選擇推斷為Double,而不是Float。
如果一個(gè)整數(shù)和一個(gè)小數(shù)相加,以算式的方式復(fù)制給一個(gè)變量,這個(gè)變量會被推斷為Double類型:
let anotherPi = 3 + 0.14159 // anotherPi是Double類型
數(shù)值類型的字面值 (Numeric Literals)
整數(shù)類型的字面值應(yīng)該這樣寫:
- 十進(jìn)制數(shù),沒有前綴
- 二進(jìn)制數(shù),需要加
0b前綴 - 八進(jìn)制數(shù),需要加
0o前綴 - 十六進(jìn)制數(shù),需要加
0x前綴
按照上面的規(guī)則,十進(jìn)制數(shù)17可以寫成:
let decimalInteger = 17
let binaryInteger = 0b10001 // 17 in binary notation
let octalInteger = 0o21 // 17 in octal notation
let hexadecimalInteger = 0x11 // 17 in hexadecimal notation
浮點(diǎn)型的字面值也可以寫成10進(jìn)制(沒有前綴),或者16進(jìn)制(0o前綴)。10進(jìn)制的浮點(diǎn)型還需要一個(gè)可選的指數(shù)e;16進(jìn)制的也需要一個(gè)指數(shù)p。
十進(jìn)制的浮點(diǎn)型有一個(gè)指數(shù)e,這個(gè)浮點(diǎn)型的值就等于基數(shù)乘以10e:
-
1.25e2就是 1.25 x 102,或者125.0。 -
1.25e-2就是 1.25 x 10-2,或者0.0125。
十六進(jìn)制的浮點(diǎn)型有一個(gè)指數(shù)p,這個(gè)浮點(diǎn)型的值就等于基數(shù)乘以2p:
-
0xFp2就是 15 x 22,或者是60.0。 -
0xFp-2就是 15 x 2-2,或者是3.75。
下面這些變量的字面值都代表著十進(jìn)制的12.1875:
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
數(shù)值類型的字面值可以包含其他格式化的字符使得這個(gè)數(shù)更易讀。整型和浮點(diǎn)型都可以加上額外的0和包含_來增強(qiáng)可讀性。這些額外增加的字符不會改變這個(gè)數(shù)的值。
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)
不同整數(shù)類型的取值范圍是不同的。例如Int8只能存儲-128127的數(shù)值;而`UInt8`只能存儲0225。如果一個(gè)數(shù)超過了這個(gè)整數(shù)類型的范圍,將會報(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 than its maximum value,
// and so this will also report an error
類型轉(zhuǎn)換的格式方法如下:
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
因?yàn)?code>one是UInt8類型,而twoThousand是UInt16類型,所以他們不能直接相加,需要把one轉(zhuǎn)換成UInt16類型,然后才可以相加。
整型和浮點(diǎn)類型轉(zhuǎn)換 (Integer and Floating-Point Conversion)
整型和浮點(diǎn)型之間的轉(zhuǎn)換必須顯示轉(zhuǎn)換:
let three = 3
let pointOneFourOneFiveNine: 0.14159
let pi = Double(three) + pointOneFourFiveNine
// pi 等于 3.14159, 并且被推斷為Double類型
浮點(diǎn)型轉(zhuǎn)為整型也必須是顯示的:
let integerPi = Int(pi)
// integerPi 等于 3, 并且被推斷為Int類型
當(dāng)浮點(diǎn)型轉(zhuǎn)為整型時(shí),浮點(diǎn)型的小數(shù)部分總是被省略掉,只取整數(shù)部分。也就是說4.75會被轉(zhuǎn)為4,-3.9被轉(zhuǎn)為-3。
類型別名 (Type Aliases)
使用typealias關(guān)鍵字來定義一個(gè)別名。當(dāng)我們想用一個(gè)更清晰更適合的類型來表達(dá)一個(gè)變量的時(shí)候,使用別名是非常有用的。例如我們從外部資源操作一個(gè)特定大小的數(shù)據(jù)時(shí):
typealias AudioSample = UInt16
定義好這個(gè)別名之后,你就可以像使用UInt16一樣地使用AudioSample:
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0
因?yàn)?code>AudioSample是一個(gè)別名,所以AudioSample.min實(shí)際上就是UInt16.min。
其實(shí)別名可以這么理解,就是為代碼的可讀性更高,更貼合項(xiàng)目實(shí)際,例如我們經(jīng)常見的TimeInterval,實(shí)際上是一個(gè)Double類型。
布爾值 (Booleans)
Swift提供了兩個(gè)布爾類型的常量:true和false:
let orangesAreOrange = true
let turnipsAreDelicious = false
orangesAreOrange和turnipsAreDelicious被推斷為Bool類型。
Swift是不允許其他不是布爾類型的值替代Bool的。例如下面這個(gè)例子就會報(bào)錯(cuò):
let i = 1
if i {
// 這個(gè)例子不能編譯通過,會報(bào)錯(cuò)
}
如果改為下面這個(gè)例子就可以編譯通過:
let i = 1
if i == 1 {
}
i == 1 的結(jié)果是Bool類型,所以第二個(gè)例子是正確的。
多元組 (Tuples)
一個(gè)多元組包含了多個(gè)元素,多元組里面的元素可以是任何類型,并且可以是不一樣的類型。例如:
let http404Error = (404, "Not Found")
http404Error這個(gè)多元組描述了HTTP的一個(gè)狀態(tài)碼,里面包含了一個(gè)Int和String,所以這個(gè)多元組的類型是(Int, String)。
我們可以拆分多元組里面的元素,例如:
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"
如果你只需要訪問其中的一個(gè)元素,可以使用_來忽略其他元素:
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"
另外,我們還可以使用下標(biāo)來訪問里面的元素:
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"
給多元組的元素命名:
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)
可選類型是Swift新增的一個(gè)類型,在OC或者C中是沒有的。當(dāng)一個(gè)值有可能為空時(shí),就是用可選類型。一個(gè)可選類型代表著兩種可能:1)有一個(gè)特定的值,并且可以把這個(gè)可選值解包得到他真正的值;2)沒有值,也就是nil。
下面舉個(gè)例子,把String轉(zhuǎn)換為Int:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
convertedNumber被推斷為Int?類型,或者說是 optional Int
因?yàn)?code>Int(possibleNumber)在轉(zhuǎn)換的時(shí)候,假如possibleNumber的字面值不是數(shù)字,那就會轉(zhuǎn)換不成功,最后返回nil給convertedNumber。所以convertedNumber的值有兩種可能,要么就轉(zhuǎn)成功,返回具體值,要么就轉(zhuǎn)換不成功,返回nil。所以convertedNumber是Int?類型。這個(gè)?的意思是變量的值可能會是nil。
nil
我們可以把nil賦值給一個(gè)可選類型:
var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value
注意:nil不能賦值給非可選類型的常量和變量。所以如果在代碼中,如果變量有可能為nil,盡量聲明為可選類型。
如果在聲明可選類型變量的時(shí)候,沒有給一個(gè)默認(rèn)值,那么這個(gè)變量默認(rèn)為nil:
var surveyAnswer: Stirng?
// surveyAnswer 自動(dòng)設(shè)置為nil
注意:Swift的nil和OC中的nil是不同的。在OC中,nil是一個(gè)指針,指向一個(gè)不存在的對象;而在Swift中,nil不是指針,只是表示一個(gè)特定類型的值是空的。任何可選類型都可以設(shè)置為nil,不僅僅是對象類型。
If語句和強(qiáng)制解包 (If Statements and Forced Unwrapping)
使用==或者!=來判斷一個(gè)可選類型的變量是否等于nil:
if covnertedNumber != nil {
print("convertedNumber contains some integer value.")
}
當(dāng)我們能確定可選類型變量確實(shí)包含了一個(gè)值,可以使用!來解包這個(gè)可選類型變量:
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
// Prints "convertedNumber has an integer value of 123."
注意:在使用!解包時(shí),一定要保證可選類型的變量有值,不能為nil,否則在運(yùn)行的時(shí)候會報(bào)錯(cuò)。
可選綁定 (Optional Binding)
使用可選綁定來確認(rèn)一個(gè)可選類型是否有值,如果有值,那么就賦值給一個(gè)局部的變量或者常量??蛇x綁定可以配合if和while語句來檢查一個(gè)可選類型變量是否有值:
if let constantName = someOptional {
// do something...
}
我們可以使用可選綁定重寫之前的這個(gè)例子:
// 之前的例子
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
// 使用可選綁定重寫,改為
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"
代碼可以這么解讀:如果Int(possibleNumber)返回的值是一個(gè)非nil的值,那么就把這個(gè)值賦值給actualNumber,然后actualNumber就可以作為局部變量在if的第一個(gè)分支語句里使用。因?yàn)?code>actualNumber已經(jīng)確定有非nil值,所以在打印那句代碼無需用!解包。
在if語句中,我們可以包含多個(gè)可選綁定和布爾條件,并且以,隔開。只要任意一個(gè)可選綁定或者任意一個(gè)布爾條件的結(jié)果為false,那么整個(gè)if語句的值將會被判斷為false:
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"
第一種寫法更為簡短。
隱式解包可選類型 (Implicitly Unwrapped Optionals)
有時(shí)候,我們能確定一個(gè)可選類型被第一次賦值后,總會有值。那我們就可以把這種可選類型聲明為隱式解包可選類型。在要設(shè)置為可選的類型名稱后面加上!,例如String!。
其實(shí)隱式解包可選類型實(shí)際上也是一個(gè)正常的可選類型,但是可以像非可選類型一樣使用,訪問它的值時(shí)不用解包。
let possibleString: String? = "一個(gè)可選類型的字符串"
let forcedString: String = possibleString! // 需要感嘆號解包
let assumedString: String! = "一個(gè)隱式解包可選類型的字符串"
let implicitString: String = assumedString // 無需感嘆號解包
注意:如果一個(gè)隱式解包可選類型是nil,然后你訪問他的解包后的值,將會在運(yùn)行的時(shí)候報(bào)錯(cuò)。
像正常的可選類型一樣,你可以使用if檢查是否有值:
if assumedString != nil {
print(assumedString)
}
// Prints "An implicitly unwrapped optional string."
也可以使用可選綁定:
if let definiteString = assumedString {
print(definiteString)
}
// Prints "An implicitly unwrapped optional string."
注意:當(dāng)一個(gè)變量有可能為nil時(shí),千萬不要使用隱式解包可選綁定。
錯(cuò)誤處理 (Error Handling)
當(dāng)一個(gè)方法遇到錯(cuò)誤條件時(shí),會拋出一個(gè)錯(cuò)誤。方法的調(diào)用者就能抓取這個(gè)錯(cuò)誤,然后進(jìn)行處理:
func canThrowAnError() throws {
// this function may or may not throw an error
}
在聲明方法時(shí),使用throws關(guān)鍵字來說明這個(gè)方法會拋出異常。當(dāng)調(diào)用一個(gè)會拋出異常的方法時(shí),使用try關(guān)鍵字。
do {
try canThrowAnError()
// no error was thrown
}
catch {
// an error was thrown
}
可以使用多個(gè)catch來抓取不同的異常:
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
在這個(gè)例子中,如果沒有干凈的盤子可用或者缺少某種做三明治的原料時(shí),makeASandwich()將會拋出異常。如果沒有異常,就會執(zhí)行eatASandwich()。如果抓取的錯(cuò)誤匹配SandwichError.outOfCleanDishes,就會執(zhí)行washDishes(),如果抓取的錯(cuò)誤匹配SandwichError.missingIngredients,就會執(zhí)行buyGroceries(_:)。
斷言 (Assertions)
調(diào)試斷言 (Debugging with Assertions)
在某些情況下,我們需要讓某些變量滿足特定的條件之后,才讓代碼繼續(xù)執(zhí)行。如果不滿足,代碼會運(yùn)行錯(cuò)誤。我們可以在代碼中使用斷言來阻止代碼執(zhí)行,然后調(diào)試錯(cuò)誤的原因。
let age = -3
assert(age >= 0, "人的年齡不能小于0")
如果age >= 0是true,那么代碼繼續(xù)執(zhí)行,如果是false,那么程序?qū)V?,并且打?code>人的年齡不能小于0。
注意: 在當(dāng)優(yōu)化優(yōu)化編譯時(shí),斷言會被禁用,例如在Xcode中使用一個(gè)程序的默認(rèn)發(fā)布配置建立新版本的時(shí)候。
何時(shí)使用斷言 (When to Use Assertions)
當(dāng)一個(gè)判斷條件有可能為false時(shí),可以使用斷言。下面這些情況可以使用斷言:
- 當(dāng)一個(gè)整數(shù)下標(biāo)被傳給一個(gè)自定義的下標(biāo)實(shí)現(xiàn)時(shí),但是下標(biāo)的值可能會太小或太大。
- 當(dāng)一個(gè)值作為參數(shù)傳給一個(gè)方法,但是一個(gè)非法的值不能讓這個(gè)方法完成它的任務(wù)。
- 一個(gè)可選類型的值目前是
nil,但是后面的代碼要求這個(gè)可選類型要有一個(gè)非nil的值。
第一部分完。下個(gè)部分:【Swift 3.1】02 - 基本運(yùn)算符 ((Basic Operators)
如果有錯(cuò)誤的地方,歡迎指正!謝謝!