級別: ★☆☆☆☆
標簽:「iOS」「Swift 5.1」「常量變量」「元組」「可選項」「可選綁定」
作者: 沐靈洛
審校: QiShare團隊
常量和變量:常量的值一旦設置就不能更改,而變量可以在將來設置為不同的值。常量和變量必須在使用之前聲明。常量聲明使用let
關鍵字。變量聲明使用var
關鍵字。
let num1 = 10 //!< 聲明一個常量num1,并且賦初始值10
var num2 = 5 //!< 聲明一個變量num2,并且賦初始值5
let a = 1, b = 3, c = 4 //!< 同時聲明多個常量并賦初值
var aa = 1, bb = 3, cc = 4 //!< 同時聲明多個變量并賦初值
類型注釋:聲明常量或變量時,可以提供類型注釋,以清楚常量或變量可以存儲的值的類型。通過在常量或變量名稱后面放置冒號,后跟空格,后跟要使用的類型的名稱來編寫類型注釋。
let num3 : Int = 3 //!<聲明一個常量num1,可以存儲的值得類型是Int類型,并且賦初始值3
var str : String = "QS" //!< 聲明一個變量str,可以存儲的值得類型是String類型,并且賦初始值"QS"
let a : Int = 1, b : [Int] = [3,4,5], c : String = "constant" //!< < 同時聲明,多個可以存儲不同類型值的常量,并賦相應類型初值
var aa : Int = 1, bb : [Int] = [3,4,5], cc : String = "change" //!< < 同時聲明,多個可以存儲不同類型值的變量,并賦相應類型初值
命名規(guī)則:常量和變量名稱幾乎可以包含任何字符,包括Unicode字符。但是常量和變量名稱不能包含空格字符,數學符號,箭頭,專用Unicode標量值或行和框繪制字符。也不能以數字開頭,但可以包含在名稱的其他地方。使用Swift關鍵字作為變量名稱時,需要使用``包裹。如下例所示:
let π = 3.14159
let 你好 = "你好世界"
let ???? = "dogcow"
let s2r = "s2r"
let `default` = "default"http://!< 使用Swift關鍵字作為變量名稱時,需要使用``包裹
一旦聲明了某個類型的常量或變量,就不能再使用相同的名稱聲明它,或者將其更改為存儲不同類型的值。也不能將常量變?yōu)樽兞炕蜃兞孔優(yōu)槌A俊?br>
打印方法:
print(_:separator:terminator:)
它可以將一個或多個值輸出到控制臺。separator和terminator參數都有默認值分別是" "
和\n
,使用時可以忽略。
let word = "張翼德"
print(word)//!< 控制臺輸出:張翼德
let p1 = "HOW"
let p2 = "ARE"
let p3 = "YOU?"
print(p1,p2,p3,separator: "...")//!< 控制臺輸出:HOW...ARE...YOU?
print(p1,p2,p3,separator: ".",terminator: "FINE THANKS\n")//!<控制臺輸出:HOW.ARE.YOU?FINE THANKS
print(_ separator:terminator:to:)
它可以將一個或多個值輸出到合適的輸出。separator和terminator參數都有默認值分別是" "
和\n
,使用時可以忽略。
let p1 = "HOW"
let p2 = "ARE"
let p3 = "YOU?"
var outputStream = ""
print(p1,p2,p3,separator: ".",terminator: "FINE THANKS\n",to:&outputStream)
print("打印的數據,寫入了outputStream變量中最終輸出為:\(outputStream)")//!< 控制臺輸出:打印的數據,寫入了outputStream變量中最終輸出為:HOW.ARE.YOU?FINE THANKS
Swift使用字符串插值將常量或變量的名稱包含在較長字符串中作為占位符,并提示Swift將其替換為該常量或變量的當前值。將名稱括在括號中,并在左括號前用反斜杠轉義它:
let word = "張翼德"
print("我乃燕人\(word)也!誰敢與我決一死戰(zhàn)?")
代碼注釋:單行注釋以兩個正斜杠開頭//
。多行注釋以正斜杠開頭后跟星號/*
,以星號后跟正斜杠*/
結束。Swift中的多行注釋可以嵌套在其他多行注釋中。
/* 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. */
分號:Swift不要求代碼中的每個語句之后都需要加分號;
我們可以寫也可以不寫。如果一行代碼中有多個語句,則必須要加分號;
整型:沒有小數部分,整數有符號(正,零或負)或無符號(正或零)。Swift提供8,16,32和64位格式的有符號和無符號整數。這些整數遵循類似于C的命名約定,因為8位無符號整數屬于類型UInt8,32位有符號整數屬于類型Int32。
整數邊界:
- 不同位數格式的有符號和無符號整數,都會有最大值和最小值。
//無符號
let unsignedMinValue = UInt8.min // UInt8類型最小值 0
let unsignedMaxValue = UInt8.max // UInt8類型最大值 255
//有符號
let minValue = Int8.min // Int8類型最小值 -128
let maxValue = Int8.max // Int8類型最大值 127
- Swift提供了一個額外的整數類型Int,它與當前平臺機器的字節(jié)位數相同。即:大多數情況下,我們不需要選擇該使用哪種大小的整數類型。除非我們需要使用特定大小的整數。每種整數類型存儲不同的值范圍,編譯代碼時,會檢查是否在指定的整數類型范圍內。在不同整數類型的數值間運算時,需要根據實際情況進行轉換。
- 在32位平臺上,Int與Int32大小相同。
- 在64位平臺上,Int與Int64大小相同。
- 在32位平臺上,UInt與UInt32大小相同。
- 在64位平臺上,UInt與UInt64大小相同。
let cannotBeNegative: UInt8 = -1 //報錯代碼:無符號的8位整數類型不能表示有符號的-1
let tooBig: Int8 = Int8.max + 1 //報錯代碼:Int8表示范圍0-255 不能表示256報錯
let twoThousand: UInt16 = 2000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
浮點數字: 浮點數是具有小數部分的數字,浮點類型可以表示比整數類型更寬范圍的值。例如3.14159,0.1,和-273.15。Swift提供了兩種帶符號的浮點數類型:Double 表示64位浮點數。 Float 表示32位浮點數。
整數與浮點數之間的轉換:
整數轉浮點數
let three = 3 //需要類型轉換,使用常量three創(chuàng)建一個double類型的新值:Double(three),否則這種相加操作不被允許會報錯
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
浮點數轉整數
let pi = 3.14159
let integerPi = Int(pi)//采用這種形式則浮點數總是被截斷,這意味著4.76->4 ,3.9->3
類型安全和類型判斷: Swift是一種類型安全的語言,編譯代碼時執(zhí)行類型檢查,并將任何不匹配的類型標記為錯誤,讓我們在開發(fā)過程中盡早捕獲并修復錯誤。比如:定義的變量或常量是String
類型的,那么我們就不能使用Int
類型賦值。所以Swift鼓勵我們在使用變量或常量時,能夠清楚的知道所需的值的類型。但并不意味著我們必須指定聲明的變量或常量的類型。如果未指定所需的值類型,Swift將使用類型推斷來匹配適當的類型。類型推斷使編譯器能夠在編譯代碼時自動推斷出特定表達式的類型,只需檢查我們提供的值即可。類型推斷有助于使Swift代碼在使用其類型已知的其他值初始化常量或變量時簡潔性和可讀性更高。
使用初始值聲明常量或變量時,類型推斷特別有用。推斷浮點數的類型時,Swift總是選擇Double
(而不是Float
)。如let π = 3.14159
,π 將會推斷為Double
類型。let π = 3 + 0.14159
,Double
類型作為加法的一部分,所以也會推斷為Double
類型
數值(浮點,整數)類型表達式:
一個十進制數(decimal),無前綴
一個二進制數(binary),有0b前綴
一個八進制數(octal),有0o前綴
一個十六進制數(hexadecimal),有0x前綴
一個整數17,使用不同進制數進行表示
let decimalInteger = 17
let binaryInteger = 0b10001 // 二進制數表示17
let octalInteger = 0o21 // 八進制數表示17
let hexadecimalInteger = 0x11 // 16進制數表示17
浮點數可以是沒有前綴的十進制數,或者帶有0x
前綴的十六進制數,不管是無前綴的十進制數還是有前綴的十六進制數它們必須始終在小數點的兩邊都有一個數字。(基于此我們可能會用到表達式去實現)十進制數有一個可選的指數,可以使用大寫或小寫的e來表示,即:en = 10?
,十六進制數也必須會有一個可選的指數,可以使用大寫或小寫的p來表示,即:pn = 2?
.
浮點數十進制數表示:
1.25e2表示1.25 * 10^2,或125.0。
1.25e-2表示1.25 * 10^ -2,或0.0125。
浮點數十六進制數表示:
0xFp2指15 * 2^2,或60.0。
0xFp-2表示15 * 2^-2,或3.75。
一個浮點數12.1875,可以使用如下表達式:
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
數字文字可以包含額外的格式以使其更易于閱讀。整數和浮點數都可以用額外的零填充,并且可以包含下劃線以幫助提高可讀性。這兩種格式都不會影響文字的基礎值:
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
定義別名:使用關鍵字:typealias
為現有類型定義別名。
typealias MyString = String
var my :MyString?
my = "我"
print(my!)
布爾:Swift有一個基本的布爾類型,叫做Bool
。Swift只提供了兩個布爾常量值:true
和false
。
元組: 元組中的值可以是任何類型,并且不必彼此具有相同的類型。我們可以創(chuàng)建任意數量,任何類型,任意順序的組合,作為一個元組。元組作為函數的返回值特別有用。
let someGrop = ("元組",401,"未授權",[1,3],true,["name" : "張三"])
print(someGrop)
分解元組中的常量或者變量單獨去使用。
let httpError = (401,"未授權")//!< httpError可以被描述為(Int,String)類型的元組
let (code,error) = httpError
print(code,error)//code:401 error 未授權
元組中包含許多項不同類型的常量或者變量,我們需要有針對性的分解出某一個元素。
let someGrop = ("元組",401,"未授權",[1,3],true,["name" : "張三"])
let (name,_,_,_,_,_) = someGrop
print(name)
使用從零開始的索引訪問元組中的各個元素值
let someGrop = ("元組",401,"未授權",[1,3],true,["name" : "張三"])
let name = someGrop.0
let num = someGrop.1
let des = someGrop.2
let array = someGrop.3
print(name,num,des,array)
定義元組時可以命名元組中各個元素。
let httpError = (code:401,error:"未授權")
//元素單獨使用
print(httpError.code,httpError.error)
定義元組時不賦初值
let httpSubError : (Int,String)?
httpSubError = (401,"未授權")
print(httpSubError!)
//也可以
let httpSubError : (code:Int,error:String)?
httpSubError = (401,"未授權")
print(httpSubError!.code)
可選項(Optionals):當某個類型變量的值可能為空的場景,我們需要使用Optionals(可選項)。一個可選代表兩種可能:有值并且你可以解開可選項(optional)的包獲取到這個值,否則沒有值。
選項的概念在C或Objective-C中不存在。Objective-C中最接近的是一個具備返回值的方法在缺少有效對象的情況下可以返回nil,否則返回此對象。但是這種場景只適用于對象 ,而對于結構體,基本C類型或枚舉值這些類型,Objective-C的方法是返回特殊值(例如NSNotFound)來表示這些類型不存在的情況。Swift的選項可以讓我們表示任何類型都沒有值的情況,而不需要特殊的常量。
對于非可選類型的常量或變量我們不能使用nil,如果我們的代碼需要在變量或常量缺少值的情況下正常運行,那么我們就應該始終聲明變量或變量為相應類型的可選值
在不提供默認值的情況下定義可選變量var str: String?
系統(tǒng)將會自動為變量設置nil
,Swift中的nil與Objective-C不同。在Objective-C中,nil
是一個指向不存在的對象的指針。在Swift中,nil
不是指針,而是一個確定的類型的變量或常量的一個缺省值。任何類型的可選值都能設置nil
,不僅僅只是對象類型
if語句和強制解包: 使用if語句通過“等于”運算符(==
)或“不等于”運算符(!=
)比較可選項,來確定可選項是否包含值nil
。
let possibleNumber = "123"
let convertedNumber : Int? = Int(possibleNumber)
if convertedNumber != nil {
//! 當我們確定了convertedNumber的值不可能為nil便可以使用!進行強制解包獲取可選包中的值
print(convertedNumber!)
強制解包可選的值:一旦確定可選項確實包含值,就可以通過在可選項名稱的末尾添加感嘆號!
來訪問其基礎值。
可選綁定:使用可選綁定
來確定可選項是否包含值,如果包含值,則這個值可用于臨時常量或變量的值。可選綁定
可與if和while語句一起使用,以檢查可選內部的值,并將該值提取為常量或變量,作為單個操作的一部分。
可選綁定,if語句示例
let possibleNumber = "1234"
let convertedNumber : Int? = Int(possibleNumber)
if let tempValue = convertedNumber {
print(tempValue);
} else {
print("沒有值")
}
if var tempVar = convertedNumber {
tempVar = tempVar + 1
print(tempVar);
} else {
print("沒有值")
}
代碼解讀:如果Int類型的可選項convertedNumber ,通過Int(possibleNumber)返回了一個值,則使用此值設置一個新的常量tempValue。條件成立時,tempValue是被一個沒有可選包的確定的值初始化的,因此沒有必要使用!
后綴來訪問它的值。
單個if語句中可以包含盡可能多的可選綁定
和布爾條件
,并以逗號分隔。如果可選綁定
中的任何值是nil,或任何布爾條件求值為false,則整個語句的條件是false。示例:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
} else {
print("不成立")
}
//等效的表達形式
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
隱式解包(未包裝)的可選項:
可選項:允許我們的常量或變量不存在,使用時,可以使用if 語句檢查是否nil
或者使用可選綁定
進行解包,以保證可選項在有值得情況下正確的訪問值。
隱式解包(未包裝)的可選項:我們從程序的結構可以得出,在我們首次設置可選項的值以后,這個可選項將總是會有值,每次調用都不可能是nil
。基于此我們可能會想,那么我們就沒必要每次訪問值時,都檢查和解包可選的值。那么上述場景中可選項,便可以被定義為隱式解包(未包裝)的可選項。
可選類型的定義是類型后加?
:(String?),而隱式解包的可選項是在類型后加!
:(String!)
隱式解包的可選項幕后其實是一個正常的可選項,但是我們可以像非可選值那樣去使用,而不用每次訪問都要先對可選項的值解包。
當使用一個字符串類型的可選項和一個隱式解包的字符串類型的可選項去訪問它們字符串類型可選包(wrapped)中的確定值會有差異。
1.定義了字符串類型的可選項,并賦初值。在使用時需要針對可選的字符串,進行強制解包,以獲取其中的確定值。需要加!
。
//! 1.定義了字符串類型的可選項,并賦初值。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 使用時需要針對可選的字符串,進行強制解包,以獲取其中的確定值。
2.定義了隱式解包的字符串類型的可選項,并賦初始值。使用時,不在需要針對隱式解包的字符串類型的可選項,進行解包。不再需要!
//! 2.定義了隱式解包的字符串類型的可選項,并賦初始值。
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 使用時,不在需要針對隱式解包的字符串類型的可選項,進行解包 即:不再需要!
3.如果隱式解包的可選項是nil
并且嘗試訪問其包裝值,則會觸發(fā)運行時錯誤。結果與在不包含值的普通可選項之后放置感嘆號完全相同。同時也可以將隱式解包的可選項視為普通可選項,以檢查它是否包含值:if
或可選綁定
。
let assumedString: String! = nil
let implicitString: String = assumedString
print(implicitString)//!< 崩潰:隱式解包一個可選項的值時發(fā)現了nil。Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
錯誤處理: 響應程序在執(zhí)行期間可能遇到的錯誤情況。
定義可以拋出錯誤的函數
func canThrowAnError() throws {
// this function may or may not throw an error
}
函數聲明時在參數與返回值之間使用關鍵字throws
,代表這個函數可能拋出錯誤。在調用這個可能拋出錯誤函數時需要在表達式前放置try
關鍵字。
當使用catch分句處理錯誤時,Swift會自動把函數拋出的錯誤,自動傳播出當前的函數作用域。
do {
try canThrowAnError()
// no error was thrown
} catch {
// an error was thrown
}
以下是如何使用錯誤處理來響應不同錯誤條件的示例:
1.定義錯誤
Error
類型:表示一個能夠被拋出(thrown)的錯誤。聲明的任何類型只要遵守了 Error
的協(xié)議都能夠在Swift的錯誤處理系統(tǒng)中表示一個錯誤。因為Error
協(xié)議沒有要求自身必須實現,我們可以在創(chuàng)建的任何自定義類型中聲明遵守這個協(xié)議即可。
public protocol Error {
}
extension Error {
}
extension Error where Self : RawRepresentable, Self.RawValue : FixedWidthInteger {
}
Error
枚舉:Swift的枚舉很適合表示簡單的錯誤。我們可以創(chuàng)建一個遵守Error
協(xié)議的枚舉類型,并在枚舉中定義每一種可能的錯誤事例。
enum HttpError: Error {
case authError
case netWorkError
case serviceError
}
2.定義方法
func requestWebData(status: String) throws -> Void {
let status = status
if status == "401" {
throw HttpError.authError
} else if status == "500" {
throw HttpError.serviceError
} else if status == "404"{
throw HttpError.netWorkError
} else {
print("一切正常");
}
}
func noThrowErrorCanDoSomeThing () {
print("沒有錯誤拋出,可以做我們要做的事情")
}
func handleServiceError() {
print("服務器錯誤,需要重新連接")
}
func handleAuthError () throws {
print("授權失敗了,選擇重新登錄")
//! 網絡請求
do {
try requestWebData(status: "404")
} catch let error as HttpError {
throw error
}
}
func handleNetWorkError(){
print("網絡錯誤了,檢查網絡")
}
3.使用do try catch
方法調用并處理異常。
do {
try requestWebData(status:"400")
//沒有出現錯誤,做事情
noThrowErrorCanDoSomeThing()
} catch HttpError.authError {
do {
try handleAuthError()
} catch HttpError.netWorkError {
print("授權登錄時出現網絡錯誤")
} catch {
print("授權登錄時出現其他錯誤")
}
} catch HttpError.netWorkError {
handleNetWorkError()
} catch HttpError.serviceError {
handleServiceError()
} catch {
print("其他錯誤")//!< 這個catch就是在補全錯誤的處理,否則編譯器指示錯誤
}
斷言和前置條件:斷言和前置條件是在運行時發(fā)生的檢查。在執(zhí)行任何進一步的代碼之前,使用它們來確保代碼滿足基本條件。如果斷言或前置條件中的布爾條件求值為true,則代碼執(zhí)行將照常繼續(xù)。如果條件評估為false,則程序的當前狀態(tài)無效,代碼執(zhí)行結束,程序終止。
斷言:只有debug
環(huán)境下才可以生效。
1.代碼未進行條件檢查
let num = -1
assert(num > 0, "num不能為負數")
2.代碼進行了條件檢查
let num = -1
if num > 10 {
} else if num >= 0 {
} else {
assertionFailure("num不能為負數")//!< 陳述條件的信息
}
前置條件:在release
環(huán)境下也可以生效。
let num = -1
precondition(num > 0, "num不能為負數")
注意:
- 如果代碼中存儲不會更改的值,需要始終使用
let
關鍵字將其聲明為常量。存儲需要更改的值需要始終使用var
關鍵字將其聲明為變量。 - 如果我們?yōu)槎x的常量或變量提供了初始值,則不需要編寫類型注釋,Swift可以進行類型推斷得出該常量或變量的類型 。若只是聲明并未賦初值,如
var str1 : String
則需要提供類型注釋 - 常量或變量命名使用與Swift關鍵字相同的名稱時,需要使用``包裹該關鍵字名稱。盡量避免使用關鍵字作為名稱。
- 除非我們需要與平臺機器的位數一致的無符號整型時使用
UInt
。其他情況Int
則優(yōu)選,即使已知要存儲的值是非負的。Int
對整數值的一致使用有助于代碼互操作性,避免在不同數字類型之間進行轉換,有利于整數類型推斷。 -
Double
至少可以精確到小數點后15位,Float
的精度可以少至6位小數。任何一種類型適當的情況下,Double
是首選。代碼中也可以根據使用的值的性質和范圍選擇適當浮點類型。 - 元組對于簡單的不同類型值的組合很有用。它們不適合創(chuàng)建復雜的數據結構。如果我們的數據結構可能更復雜,應當使用構建為類或結構,而不是元組。
- 嘗試使用
!
訪問不存在的可選值會觸發(fā)運行時錯誤。在使用!
強制解包可選變量或常量的值之前,請務必確保可選項包含的值非nil
。 - 在if語句中使用
可選綁定
創(chuàng)建的常量和變量僅在if語句的主體中可用。相反,使用guard語句創(chuàng)建的常量和變量在主體語句后面的代碼行中仍然可用。
let possibleNumber = "1234"
let convertedNumber : Int? = Int(possibleNumber)
guard let tempValue = convertedNumber else {
print("沒有值")
//只是跳出方法體
return
}
print("guard\(tempValue)")
- 當變量在后面使用時可能變?yōu)閚il,不要使用隱式解包的可選項。如果需要在變量的生命周期內檢查值是否為
nil
,請始終使用普通的可選類型。
var assumedString: String! = "俺是張飛"
assumedString = nil;
let implicitString: String = assumedString //< 崩潰:隱式解包一個可選項的值時發(fā)現了nil。Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
- 使用throws關鍵字表明這個方法會拋出異常,所以需要try catch 語句進行錯誤處理,否則方法直接調用不會被允許,會報錯。同時對于錯誤的處理(catch)必須是全面的(窮盡所有可能出現的錯誤),否則此處的錯誤不會被執(zhí)行,編譯器會指示錯誤。
參考資料:
swift 5.1官方編程指南
推薦文章:
iOS UI狀態(tài)保存和恢復(三)
iOS UI狀態(tài)保存和恢復(二)
iOS UI狀態(tài)保存和恢復(一)
iOS 中精確定時的常用方法
Sign In With Apple(一)
算法小專欄:動態(tài)規(guī)劃(一)
Dart基礎(一)
Dart基礎(二)
Dart基礎(三)
Dart基礎(四)