基礎(chǔ)知識(shí) (The Basics)
自從蘋果2014年發(fā)布Swift毒坛,到現(xiàn)在已經(jīng)兩年多了佑吝,而Swift也來到了3.1版本。去年利用工作之余宪摧,共花了兩個(gè)多月的時(shí)間把官方的Swift編程指南看完∏蚣埃現(xiàn)在整理一下筆記氧骤,回顧一下以前的知識(shí)。有需要的同學(xué)可以去看官方文檔>>吃引。
常量和變量 (Constants and Variables)
聲明常量和變量 (Declaring Constants and Variables)
使用let
來聲明常量筹陵,用var
來聲明變量。例如下面的常量和變量用來跟蹤用戶的輸入的密碼次數(shù):
let maximumNumberOfLoginAttemps = 10
var currentLoginAttemps = 0
當(dāng)然镊尺,我們還可以用一行代碼中聲明多個(gè)常量或者變量朦佩,并且用逗號(hào)隔開:
var x = 0.0, y = 0.0, z = 0.0
注意:如果在代碼中,你存儲(chǔ)的一個(gè)值永遠(yuǎn)不會(huì)變庐氮,請(qǐng)一定使用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é)符號(hào)、箭頭弱卡、私有(或非法)的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. */
分號(hào) (Semicolons)
不同于其他語言条获,Swift在每行代碼后面不需要加分號(hào)忠荞。
整數(shù) (Integers)
Swift提供了8、16帅掘、32和64位的整數(shù)委煤。這些整數(shù)的命名和C語言類似,例如8位無符號(hào)整數(shù)是UInt8
修档,32位有符號(hào)整數(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)行平臺(tái):
- 在32-bit的平臺(tái)上,
Int
相當(dāng)于Int32
- 在64-bit的平臺(tái)上照激,
Int
相當(dāng)于Int64
注意:當(dāng)你真的特別需要無符號(hào)的整數(shù)類型時(shí)才使用Unit
发魄,否則一般情況下是推薦使用Int
,即使你知道你要存儲(chǔ)的值是非負(fù)數(shù)俩垃。一致使用Int
能增強(qiáng)代碼的互動(dòng)性励幼,可以避免不同數(shù)字類型的轉(zhuǎn)換欢策。
UInt
Swift也提供了不帶符號(hào)的整型,UInt
赏淌,他的取值范圍也取決于當(dāng)前運(yùn)行平臺(tái):
- 在32-bit的平臺(tái)上踩寇,
UInt
相當(dāng)于UInt32
- 在64-bit的平臺(tái)上,
UInt
相當(dāng)于UInt64
浮點(diǎn)型 (Floating-Point Numbers)
浮點(diǎn)型的變量存儲(chǔ)的值范圍比整數(shù)型更廣六水。Swift提供了兩種有符號(hào)的浮點(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會(huì)選擇推斷為Double,而不是Float衩椒。
如果一個(gè)整數(shù)和一個(gè)小數(shù)相加蚌父,以算式的方式復(fù)制給一個(gè)變量,這個(gè)變量會(huì)被推斷為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)可讀性像鸡。這些額外增加的字符不會(huì)改變這個(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
只能存儲(chǔ)-128127的數(shù)值只估;而`UInt8`只能存儲(chǔ)0225。如果一個(gè)數(shù)超過了這個(gè)整數(shù)類型的范圍着绷,將會(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 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會(huì)被轉(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è)例子就會(huì)報(bào)錯(cuò):
let i = 1
if i {
// 這個(gè)例子不能編譯通過,會(huì)報(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"
多元組作為方法的返回值是特別有用的,我們?cè)诜椒ɡ锟梢苑祷馗嗟男畔ⅰ?/p>
可選類型 (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ù)字,那就會(huì)轉(zhuǎn)換不成功疲扎,最后返回nil
給convertedNumber
昵时。所以convertedNumber
的值有兩種可能,要么就轉(zhuǎn)成功椒丧,返回具體值壹甥,要么就轉(zhuǎn)換不成功,返回nil
壶熏。所以convertedNumber是Int?
類型句柠。這個(gè)?
的意思是變量的值可能會(huì)是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è)不存在的對(duì)象;而在Swift中鹰贵,nil
不是指針晴氨,只是表示一個(gè)特定類型的值是空的。任何可選類型都可以設(shè)置為nil
碉输,不僅僅是對(duì)象類型籽前。
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í)候會(huì)報(bào)錯(cuò)阻荒。
可選綁定 (Optional Binding)
使用可選綁定來確認(rèn)一個(gè)可選類型是否有值挠锥,如果有值,那么就賦值給一個(gè)局部的變量或者常量侨赡”妥猓可選綁定可以配合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語句的值將會(huì)被判斷為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è)可選類型被第一次賦值后,總會(huì)有值情竹。那我們就可以把這種可選類型聲明為隱式解包可選類型。在要設(shè)置為可選的類型名稱后面加上!
匀哄,例如String!
秦效。
其實(shí)隱式解包可選類型實(shí)際上也是一個(gè)正常的可選類型,但是可以像非可選類型一樣使用涎嚼,訪問它的值時(shí)不用解包阱州。
let possibleString: String? = "一個(gè)可選類型的字符串"
let forcedString: String = possibleString! // 需要感嘆號(hào)解包
let assumedString: String! = "一個(gè)隱式解包可選類型的字符串"
let implicitString: String = assumedString // 無需感嘆號(hào)解包
注意:如果一個(gè)隱式解包可選類型是nil
,然后你訪問他的解包后的值法梯,將會(huì)在運(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í)姻灶,會(huì)拋出一個(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è)方法會(huì)拋出異常。當(dāng)調(diào)用一個(gè)會(huì)拋出異常的方法時(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()
將會(huì)拋出異常塞俱。如果沒有異常,就會(huì)執(zhí)行eatASandwich()
吏垮。如果抓取的錯(cuò)誤匹配SandwichError.outOfCleanDishes
障涯,就會(huì)執(zhí)行washDishes()
,如果抓取的錯(cuò)誤匹配SandwichError.missingIngredients
惫皱,就會(huì)執(zhí)行buyGroceries(_:)
像樊。
斷言 (Assertions)
調(diào)試斷言 (Debugging with Assertions)
在某些情況下,我們需要讓某些變量滿足特定的條件之后旅敷,才讓代碼繼續(xù)執(zhí)行生棍。如果不滿足,代碼會(huì)運(yùn)行錯(cuò)誤媳谁。我們可以在代碼中使用斷言來阻止代碼執(zhí)行涂滴,然后調(diào)試錯(cuò)誤的原因。
let age = -3
assert(age >= 0, "人的年齡不能小于0")
如果age >= 0
是true
晴音,那么代碼繼續(xù)執(zhí)行柔纵,如果是false
,那么程序?qū)?huì)停止锤躁,并且打印人的年齡不能小于0
搁料。
注意: 在當(dāng)優(yōu)化優(yōu)化編譯時(shí),斷言會(huì)被禁用系羞,例如在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)的值可能會(huì)太小或太大。
- 當(dāng)一個(gè)值作為參數(shù)傳給一個(gè)方法澎迎,但是一個(gè)非法的值不能讓這個(gè)方法完成它的任務(wù)庐杨。
- 一個(gè)可選類型的值目前是
nil
选调,但是后面的代碼要求這個(gè)可選類型要有一個(gè)非nil
的值。
第一部分完灵份。下個(gè)部分:【Swift 3.1】02 - 基本運(yùn)算符 ((Basic Operators)
如果有錯(cuò)誤的地方仁堪,歡迎指正!謝謝各吨!