對於使用Objective-C開發iOS的程式設計師來說,物件是否為nil在編寫程式的過程中程式設計師不太關心,直接使用就可以,在一些需要判斷的地方在判斷,當轉為Swift開發時,首先頭疼的問題就是可選型別,到底何時使用?,何時使用!總是拿不太準,下面就詳細的講解一下
一、什麼是可選型別
可選型別伴隨著Swift誕生,在原有的Objective-C語言中不存在,究其原因,是因為Swift是型別安全的語言,而OC則是弱型別語言,OC中 str字串既可以是nil,也可以是字串,而Swift中,這兩種狀態是不能同時存在的。 OC和Swift對於nil的解釋也是不太一樣的
1.Objective-C中的nil:表示缺少一個合法的物件,是指向不存在物件的指標,對結構體、列舉等型別不起作用(會返回NSNotFound) 2.Swift中的nil:表示任意型別的值缺失,是一個確定的值,要麼是該型別的一個值要麼什麼都沒有(即為nil)
在Swift中Optional(可選型別)是一個含有兩種情況的列舉,None 和 Some(T),用來表示可能有或可能沒有值。任何型別都可以明確宣告為(或者隱式轉換)可選型別。當宣告一個可選型別的時候,要確保用括號給 ? 操作符一個合適的範圍。
1.1可選型別的宣告
var optionalStr: String? = "swift語言可選型別"//宣告可選型別字串,並賦初值
var opStu:Student? //宣告可選opStu物件,賦初值nil
複製程式碼
注意:在型別和 ?之間沒有空格
1.2 強制解析(拆包)
當你確定自定義的可選型別一定有值時,可以使用操作符(!)進行強制解析,拿到資料,歎號表示"我知道一定有值,請使用它",但是當你判斷錯誤,可選值為nil時使用(!)進行強制解析,會有執行錯誤。
var myStr:String? = nil
myStr="強制解析,一定有值"
if myStr != nil {
print(myStr!)//使用!進行強制解析
}else{
print("字串為nil")
}
複製程式碼
執行結果
強制解析,一定有值
複製程式碼
1.3 自動解析
在最初的宣告時使用?修飾,當你希望它自動解析是可以用!代替?來修飾,這樣在變數使用時就不需要加!來強制拆包,它會自動解析。
var myStr:String! //使用!修飾
myStr="自動解析"
if myStr != nil {
print(myStr)
}else{
print("字串為nil")
}
複製程式碼
執行結果
自動解析
複製程式碼
1.4 可選繫結
A.使用可選繫結,擺脫了頻繁的判斷是否為nil在賦值,但是使用可選繫結(optional binding)來判斷可選型別是否包含值,如果包含就把值賦給一個臨時常量或者變數。可選繫結可以用在if和while語句中來對可選型別的值進行判斷並把值賦給一個常量或者變數。 格式:
if let tempStr = someOptional{
tempStr//如果someOptiona有值的話,就會賦值給tempStr,然後使用
}
複製程式碼
var myStr:String?
myStr="可選繫結"
if let tempStr = myStr {
print(tempStr)
}else{
print("字串為nil")
}
複製程式碼
執行結果
可選繫結
複製程式碼
B.在可選型別判斷中還有guard let的用法,guard名為守衛的意思。guard let和if let剛好相反,guard let守護一定有值。如果沒有,直接返回
let name: String? = "張三"
let age: Int? = 10
guard let nameNew = name,
let ageNew = age
else {
print("姓名 或 年齡 為nil")
return
}
print("姓名 或 年齡 為\(name),\(age)")//執行到這裡是一定有值的
複製程式碼
1.5 as! 與 as? 的型別轉換
在寫Swift程式碼時總是拿不準到底使用哪個,有時會根據編輯器的提示去Fix,但是這樣不能很好的解決問題。 1.as!使用場合
向下轉型(Downcasting)時使用。由於是強制型別轉換,如果轉換失敗會報 runtime 執行錯誤。就是說強制從父類轉換成子類
class Animal {}
class Cat: Animal {}
let animal :Animal = Cat()
let cat = animal as! Cat
複製程式碼
2.as?使用場合
as? 和 as! 操作符的轉換規則完全一樣。但 as? 如果轉換不成功的時候便會返回一個 nil 物件。成功的話返回可選型別值(optional),需要我們拆包使用。由於 as? 在轉換失敗的時候也不會出現錯誤,所以對於如果能確保100%會成功的轉換則可使用 as!,否則使用 as?
let animal:Animal = Cat()
if let cat = animal as? Cat{
print("cat is not nil")
} else {
print("cat is nil")
}
複製程式碼
二、可選型別的使用場景
2.1 函式或方法的返回型別為可選型別
在方法中返回值如Int?、String?、(Int, String)?、[Int]?、[Int: String]?等
func returnOptionValue(value: Bool) -> String? { // 返回型別為可選String型別
if value {
return "返回型別是可選型別值" // 如果為真,返回Int型別的值1
} else {
return nil //返回nil
}
}
let optionValue = returnOptionValue(value: true) // 要用可選繫結判斷再使用,因為returnOptionValue為String?可選型別
if let value = optionValue {
print(value)
} else {
print("none value")
}
複製程式碼
執行結果
返回型別是可選型別值
複製程式碼
返回型別為閉包可選
func returnOptionalFunc(value: Bool) -> (() -> (Void))? { // 返回型別為可選型別的閉包
if value {
return { () in
print("返回型別是可選型別閉包")
}
} else {
return nil
}
}
let possibleFunc = returnOptionalFunc(value: true) // 要用可選繫結判斷再使用,因為possibleFunc 為可選型別的閉包,型別為() -> (Void)
if let aFunc = possibleFunc {
print(aFunc()) // 注意增加()呼叫閉包,因為沒有引數則是空括號
} else {
print("none func")
}
複製程式碼
執行結果
返回型別是可選型別閉包
複製程式碼
2.2 可選型別在類或結構體中的運用
可選型別在類中的使用
class PossibleClass {
var someValue: Int
var possibleValue: String? // 可選儲存屬性,預設值為nil
init(someValue: Int) { // 構造方法中可以不對possibleValue屬性初始化
self.someValue = someValue
}
}
let someClass = PossibleClass(someValue: 4)
複製程式碼
可選型別在結構體中的運用
struct PossibleStruct {
var someValue: Int
var possibleValue: String? // 可選儲存屬性,預設值為nil
// 結構體中可以自定義一個init構造器對屬性初始化,也可以不自定義
}
let someStruct = PossibleStruct(someValue: 4, possibleValue: nil)
複製程式碼
2.3 可選型別在構造器中使用
class PossibleStructInit {
let someValue: String
init?(someValue: String) { // 可失敗構造器
if someValue.isEmpty { return nil } // 如果例項化為空串,則返回nil(即例項化失敗)
self.someValue = someValue
}
}
let oneStruct = PossibleStructInit(someValue: "構造器中使用") // abc不是空串,oneStruct為PossibleStructInit?可選型別
if let one = oneStruct { // 使用if let可選繫結判斷
print(one.someValue)
} else {
print("none value")
}
//輸入結果為:構造器中使用
let twoStruct = PossibleStructInit(someValue: "") // 傳參為空串
if let two = twoStruct {
print(two.someValue)
} else {
print("none value")
}
//輸入結果為:none value
複製程式碼
2.4 可選型別在錯誤處理中使用(try!與try?)
1.try?會將錯誤轉換為可選值,當呼叫try?+函式或方法語句時候,如果函式或方法丟擲錯誤,程式不會發崩潰,而返回一個nil,如果沒有丟擲錯誤則返回可選值。 2.使用try!可以打破錯誤傳播鏈條。錯誤丟擲後傳播給它的呼叫者,這樣就形成了一個傳播鏈條,但有的時候確實不想讓錯誤傳播下去,可以使用try!語句
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
// 上述語句中在執行loadImage方法時如果執行失敗,使用try!來禁用錯誤傳遞,會有執行錯誤導致App崩潰
複製程式碼
func someThrowingFunction() throws -> Int {
// ...
}
let x = try? someThrowingFunction() // x可能正常返回一個Int型別的值也有可能丟擲一個錯誤異常,使用時對x用if let可選繫結判斷
複製程式碼