【Swift 3.1】01 - 基礎(chǔ)知識 (The Basics)

基礎(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è)全局方法,separatorterminator參數(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)

我們可以使用minmax屬性來訪問各個(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類型,而twoThousandUInt16類型,所以他們不能直接相加,需要把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è)布爾類型的常量:truefalse

let orangesAreOrange = true
let turnipsAreDelicious = false

orangesAreOrangeturnipsAreDelicious被推斷為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è)IntString,所以這個(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)換不成功,最后返回nilconvertedNumber。所以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綁定可以配合ifwhile語句來檢查一個(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 >= 0true,那么代碼繼續(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ò)誤的地方,歡迎指正!謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容