在Optional.Swift源碼中,Optional定義如下。

-
Optional是通過(guò)enum實(shí)現(xiàn)的,Optional本質(zhì)是枚舉。 - 有兩個(gè)
case,nil和some。 - 關(guān)聯(lián)值就是傳進(jìn)來(lái)的值。
本質(zhì)上?是語(yǔ)法糖,下面的寫(xiě)法等價(jià):
var age: Int? = 10
var age1:Optional<Int> = Optional(10)
switch age {
case .none:
print("nil")
case .some(10):
print("\(10)")
default:
print("unKnown")
}
print(age == age1)
10
true
強(qiáng)制解包
var age: Int? = nil
print(age!)
如果age沒(méi)有值!會(huì)直接crash。Fatal error: Unexpectedly found nil while unwrapping an Optional value:
if let/guard let 可選綁定
if let
var age: Int? = nil
//相當(dāng)于把a(bǔ)ge變量的值拿出來(lái)給到temp
if let temp = age {
print("\(temp)")
} else {
print("nil")
}
temp作用域在if中。
guard let
var age: Int? = 10
func test() -> Any {
guard let temp = age else {
return "error"
}
print(temp)
return temp
}
test()
10
temp作用和guard同級(jí),可以在guard外部訪問(wèn)到temp。
init
@_transparent
public init(_ some: Wrapped) { self = .some(some) }
@_transparent
public init(nilLiteral: ()) {
self = .none
}
.some(some)給了self。
Equatable
Optional 遵循了Equatable 協(xié)議,重寫(xiě)了==方法
extension Optional: Equatable where Wrapped: Equatable {
@inlinable
public static func ==(lhs: Wrapped?, rhs: Wrapped?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l == r
case (nil, nil):
return true
default:
return false
}
}
}
前面age和age1能夠比較就是因?yàn)?code>Optional遵循了Equatable協(xié)議重載了==運(yùn)算符。
var age: Int? = 10
var age1:Optional<Int> = Optional(10)
print(age == age1)
如果我們自定義數(shù)據(jù)類(lèi)型要進(jìn)行==需要遵循Equatable協(xié)議:
struct HotpotCat {
var age: Int
var name: String
}
extension HotpotCat: Equatable {}
var hp = HotpotCat(age: 18, name: "hotpot")
var hp1 = HotpotCat(age: 18, name: "hotpot")
print(hp == hp1)
true
在HotpotCat中我們并沒(méi)有實(shí)現(xiàn)==方法,編譯器幫我們默認(rèn)實(shí)現(xiàn)了,看下SIL實(shí)現(xiàn):

__derived_struct_equals實(shí)現(xiàn):
// static HotpotCat.__derived_struct_equals(_:_:)
sil hidden @static main.HotpotCat.__derived_struct_equals(main.HotpotCat, main.HotpotCat) -> Swift.Bool : $@convention(method) (@guaranteed HotpotCat, @guaranteed HotpotCat, @thin HotpotCat.Type) -> Bool {
// %0 "a" // users: %13, %6, %3
// %1 "b" // users: %15, %7, %4
// %2 "self" // user: %5
bb0(%0 : $HotpotCat, %1 : $HotpotCat, %2 : $@thin HotpotCat.Type):
//兩個(gè)變量分別給到a和b(HotpotCat結(jié)構(gòu)體)
debug_value %0 : $HotpotCat, let, name "a", argno 1 // id: %3
debug_value %1 : $HotpotCat, let, name "b", argno 2 // id: %4
debug_value %2 : $@thin HotpotCat.Type, let, name "self", argno 3 // id: %5
//結(jié)構(gòu)體中取出age(Int)
%6 = struct_extract %0 : $HotpotCat, #HotpotCat.age // user: %8
%7 = struct_extract %1 : $HotpotCat, #HotpotCat.age // user: %9
//Int結(jié)構(gòu)體中取出value
%8 = struct_extract %6 : $Int, #Int._value // user: %10
%9 = struct_extract %7 : $Int, #Int._value // user: %10
//比較
%10 = builtin "cmp_eq_Int64"(%8 : $Builtin.Int64, %9 : $Builtin.Int64) : $Builtin.Int1 // user: %11
//相等bb1,否則bb4
cond_br %10, bb1, bb4 // id: %11
bb1: // Preds: bb0
%12 = metatype $@thin String.Type // user: %18
//HotpotCat結(jié)構(gòu)體中取出name
%13 = struct_extract %0 : $HotpotCat, #HotpotCat.name // users: %20, %18, %14
retain_value %13 : $String // id: %14
%15 = struct_extract %1 : $HotpotCat, #HotpotCat.name // users: %19, %18, %16
retain_value %15 : $String // id: %16
// function_ref static String.== infix(_:_:)
//String自己的==方法
%17 = function_ref @static Swift.String.== infix(Swift.String, Swift.String) -> Swift.Bool : $@convention(method) (@guaranteed String, @guaranteed String, @thin String.Type) -> Bool // user: %18
%18 = apply %17(%13, %15, %12) : $@convention(method) (@guaranteed String, @guaranteed String, @thin String.Type) -> Bool // user: %21
release_value %15 : $String // id: %19
release_value %13 : $String // id: %20
//比較是否相同
%21 = struct_extract %18 : $Bool, #Bool._value // user: %22
cond_br %21, bb2, bb3 // id: %22
bb2: // Preds: bb1
//構(gòu)造bool值-1
%23 = integer_literal $Builtin.Int1, -1 // user: %24
%24 = struct $Bool (%23 : $Builtin.Int1) // user: %25
br bb5(%24 : $Bool) // id: %25
bb3: // Preds: bb1
//構(gòu)造bool值0
%26 = integer_literal $Builtin.Int1, 0 // user: %27
%27 = struct $Bool (%26 : $Builtin.Int1) // user: %28
br bb5(%27 : $Bool) // id: %28
bb4: // Preds: bb0
//構(gòu)造bool值0
%29 = integer_literal $Builtin.Int1, 0 // user: %30
%30 = struct $Bool (%29 : $Builtin.Int1) // user: %31
br bb5(%30 : $Bool) // id: %31
// %32 // user: %33
bb5(%32 : $Bool): // Preds: bb2 bb3 bb4
//返回上面構(gòu)造的bool值
return %32 : $Bool // id: %33
} // end sil function 'static main.HotpotCat.__derived_struct_equals(main.HotpotCat, main.HotpotCat) -> Swift.Bool'
- 1.取兩個(gè)結(jié)構(gòu)體
a、b - 2.比較
a.age與b.age - 3.取
a.name與b.name,并且調(diào)用String自己的判等方法 - 4.構(gòu)造
Int1類(lèi)型的數(shù)據(jù),相等-1,不相等0。
底層通過(guò)Int1類(lèi)型的數(shù)據(jù)生成Bool類(lèi)型:
%29 = integer_literal $Builtin.Int1, 0 // user: %30
%30 = struct $Bool (%29 : $Builtin.Int1) // user: %31
所以也可以簡(jiǎn)單說(shuō)Swift中的Bool是-1(true)和0(false)。
Bool.Swift源碼:

上面我們分析的是結(jié)構(gòu)體,那么如果HotpotCat是class呢?

可以看到這個(gè)時(shí)候需要我們自己實(shí)現(xiàn)
==方法了。
class HotpotCat {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
}
extension HotpotCat: Equatable {
static func == (lhs: HotpotCat, rhs: HotpotCat) -> Bool {
return lhs.age == rhs.age && lhs.name == rhs.name
}
}
var hp = HotpotCat.init(age: 18, name: "hotpot")
var hp1 = HotpotCat.init(age: 18, name: "hotpot")
var hp2 = hp
//比較值相等
print(hp == hp1)
//比較地址
print(hp === hp1)
print(hp === hp2)
true
false
true
Comparable
public protocol Comparable : Equatable {
static func < (lhs: Self, rhs: Self) -> Bool
static func <= (lhs: Self, rhs: Self) -> Bool
static func >= (lhs: Self, rhs: Self) -> Bool
static func > (lhs: Self, rhs: Self) -> Bool
}
Comparable自動(dòng)實(shí)現(xiàn)了Equatable
struct HotpotCat {
var age: Int
var name: String
}
extension HotpotCat: Comparable {
static func < (lhs: HotpotCat, rhs: HotpotCat) -> Bool {
return lhs.age < rhs.age && lhs.name < lhs.name
}
}
var hp = HotpotCat(age: 21, name: "hotpot1")
var hp1 = HotpotCat(age: 20, name: "hotpot")
print(hp > hp1)
在這里結(jié)構(gòu)體必須實(shí)現(xiàn)<,編譯器會(huì)通過(guò)<自動(dòng)實(shí)現(xiàn)其它運(yùn)算符。當(dāng)然實(shí)現(xiàn)>,不實(shí)現(xiàn)<不行,由于源碼中其它運(yùn)算法是通過(guò)<來(lái)實(shí)現(xiàn)的:
public protocol Comparable: Equatable {
static func < (lhs: Self, rhs: Self) -> Bool
static func <= (lhs: Self, rhs: Self) -> Bool
static func >= (lhs: Self, rhs: Self) -> Bool
static func > (lhs: Self, rhs: Self) -> Bool
}
extension Comparable {
@inlinable
public static func > (lhs: Self, rhs: Self) -> Bool {
return rhs < lhs
}
@inlinable
public static func <= (lhs: Self, rhs: Self) -> Bool {
return !(rhs < lhs)
}
@inlinable
public static func >= (lhs: Self, rhs: Self) -> Bool {
return !(lhs < rhs)
}
}
??
??運(yùn)算符在optional源碼中有兩個(gè):
@_transparent
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T)
rethrows -> T {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
@_transparent
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?)
rethrows -> T? {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
一個(gè)返回T,一個(gè)返回T?,這里為什么有兩個(gè)?看個(gè)例子就明白了
var age: Int? = nil
var age2: Int? = 10
var temp = age ?? age2
print(temp)
可以看到??的返回值是和age2相關(guān)的。age2是什么類(lèi)型就返回什么類(lèi)型,age2是可選類(lèi)型就返回T?,否則返回T。
可選鏈
可選鏈?zhǔn)且粋€(gè)調(diào)用和查詢可選屬性、方法和下標(biāo)的過(guò)程,它可能為 nil 。如果可選項(xiàng)包含值,屬性、方法或者下標(biāo)的調(diào)用成功;如果可選項(xiàng)是 nil ,屬性、方法或者下標(biāo)的調(diào)用會(huì)返回 nil 。多個(gè)查詢可以鏈接在一起,如果鏈中任何一個(gè)節(jié)點(diǎn)是 nil ,那么整個(gè)鏈就會(huì)得體地失敗。
class Hotpot {
var name: String?
var cat: Cat?
func test() {
print("test")
}
}
class Cat {
var catName: String?
func ctTest() {
print("ctTest")
}
}
var cat: Cat? = nil
var hp: Hotpot? = Hotpot()
let temp = hp?.cat?.catName
hp?.test()
hp?.cat?.ctTest()
test
unsafelyUnwrapped
在optional源碼中還有一個(gè)unsafelyUnwrapped,實(shí)現(xiàn)如下:
/// The wrapped value of this instance, unwrapped without checking whether
/// the instance is `nil`.
///
/// The `unsafelyUnwrapped` property provides the same value as the forced
/// unwrap operator (postfix `!`). However, in optimized builds (`-O`), no
/// check is performed to ensure that the current instance actually has a
/// value. Accessing this property in the case of a `nil` value is a serious
/// programming error and could lead to undefined behavior or a runtime
/// error.
///
/// In debug builds (`-Onone`), the `unsafelyUnwrapped` property has the same
/// behavior as using the postfix `!` operator and triggers a runtime error
/// if the instance is `nil`.
///
/// The `unsafelyUnwrapped` property is recommended over calling the
/// `unsafeBitCast(_:)` function because the property is more restrictive
/// and because accessing the property still performs checking in debug
/// builds.
///
/// - Warning: This property trades safety for performance. Use
/// `unsafelyUnwrapped` only when you are confident that this instance
/// will never be equal to `nil` and only after you've tried using the
/// postfix `!` operator.
@inlinable
public var unsafelyUnwrapped: Wrapped {
@inline(__always)
get {
if let x = self {
return x
}
_debugPreconditionFailure("unsafelyUnwrapped of nil optional")
}
}
unsafelyUnwrapped 和!強(qiáng)制解包一樣。
var age: Int? = 20
print(age.unsafelyUnwrapped)
區(qū)別在于:

對(duì)于第2點(diǎn),在
release -Onone模式下驗(yàn)證下:
可以看到
age.unsafelyUnwrapped為0。
as as? as!
var age: Int = 10
var age1 = age as Any
print(age1)
var age2 = age as? Double
print(age2)
var age3 = age as! Double
print(age3)
10
nil
SwiftClouse was compiled with optimization - stepping may behave oddly; variables may not be available.

訪問(wèn)控制
在OC中很少用到訪問(wèn)控制,Swift中主要針對(duì)其它源文件和模塊對(duì)代碼的訪問(wèn)控制。
Swift為代碼的實(shí)體提供個(gè)五個(gè)不同的訪問(wèn)級(jí)別。這些訪問(wèn)級(jí)別和定義實(shí)體的源文件相關(guān),并且也和源文件所屬的模塊相關(guān)。如果不指明訪問(wèn)級(jí)別的話,代碼中的所有實(shí)體都會(huì)默認(rèn)為 internal 級(jí)別。大多數(shù)情況下不需要明確指定訪問(wèn)級(jí)別。訪問(wèn)控制權(quán)限
private
將實(shí)體的使用限制于封閉聲明中。當(dāng)一些細(xì)節(jié)僅在單獨(dú)的聲明中使用時(shí),使用 private 訪問(wèn)隱藏特定功能的實(shí)現(xiàn)細(xì)節(jié)。
僅在聲明的作用域有效

fileprivate
將實(shí)體的使用限制于當(dāng)前定義源文件中。當(dāng)一些細(xì)節(jié)在整個(gè)文件中使用時(shí),使用 file-private 訪問(wèn)隱藏特定功能的實(shí)現(xiàn)細(xì)節(jié)。
僅限制在當(dāng)前定義的源文件中
這個(gè)時(shí)候我們?cè)诹硗庖粋€(gè)文件新建一個(gè)SubHotpotCat:
class SubHotpotCat: HotpotCat {
fileprivate var name:String = "subHotpotCat"
}
fileprivate func test() {
let subHotpot = SubHotpotCat()
print(subHotpot.name)
}
在HotpotCat文件中訪問(wèn)subHotpot.name:

internal
允許實(shí)體被定義模塊中的任意源文件訪問(wèn),但不能被該模塊之外的任何源文件訪問(wèn)。通常在定義應(yīng)用程序或是框架的內(nèi)部結(jié)構(gòu)時(shí)使用。
默認(rèn)的訪問(wèn)級(jí)別,允許定義模塊中的任意源文件訪問(wèn),但不能被該模塊之外的任何源文件訪問(wèn)。
這里的模塊是指:一個(gè)框架或者應(yīng)用程序。主要指import關(guān)鍵字導(dǎo)入的模塊
import Foundation
上面的Foundation就是模塊。三方庫(kù)也是模塊。
public
允許實(shí)體被定義模塊中的任意源文件訪問(wèn),同樣可以被另一模塊的源文件通過(guò)導(dǎo)入該定義模塊來(lái)訪問(wèn)。在指定框架的公共接口時(shí),通常使用 open 或 public 訪問(wèn)。
開(kāi)放式訪問(wèn),能夠在其定義模塊的任何源文件中使用代碼,并且可以從另外一個(gè)源文件訪問(wèn)源文件。
只能在定義的模塊中繼承和子類(lèi)重寫(xiě)。
open
最不受限制的訪問(wèn)級(jí)別
open 訪問(wèn)僅適用于類(lèi)和類(lèi)成員,它與 public 訪問(wèn)區(qū)別如下:
- public 訪問(wèn),或任何更嚴(yán)格的訪問(wèn)級(jí)別的類(lèi),只能在其定義模塊中被繼承。
- public 訪問(wèn),或任何更嚴(yán)格訪問(wèn)級(jí)別的類(lèi)成員,只能被其定義模塊的子類(lèi)重寫(xiě)。
- open 類(lèi)可以在其定義模塊中被繼承,也可在任何導(dǎo)入定義模塊的其他模塊中被繼承。
- open 類(lèi)成員可以被其定義模塊的子類(lèi)重寫(xiě),也可以被導(dǎo)入其定義模塊的任何模塊重寫(xiě)。
顯式地標(biāo)記類(lèi)為 open 意味著你考慮過(guò)其他模塊使用該類(lèi)作為父類(lèi)對(duì)代碼的影響,并且相應(yīng)地設(shè)計(jì)了類(lèi)的代碼。