你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> Swift Optionals 源碼解析

Swift Optionals 源碼解析

編輯:IOS開發基礎

Swift 引入的 Optional,很好的解決了 Objective-C 時代 “nil or not nil” 的問題,配合 Type-Safe 特性,幫我們減少了很多隱藏的問題。下面讓我們通過源碼來了解一下 Optional 具體的實現邏輯。

初識 Optional

Swift 中的 Optional 想要表達的涵義更多是“有沒有”,而非“空不空”,因此在 Swift 中,對 nil 的使用也就不像 Objective-C 中那麼隨意。我們需要將其聲明為 Optional(可能沒有),才能將其賦值為 nil(沒有),也正因為表達的是“有沒有”,所以可選類型也就不局限於對象類型了,基本數據類型同樣可以聲明 Optional,並賦值為 nil。

基礎用法

Swift 聲明一個 Optional 變量方法如下:

Swift

var optionalValue: Optional

另外,還有一個更常見的聲明方式,即 ?:

Swift
var optionalValue: T?

上面兩種方式是等價的,我們得到了一個類型為 T 的可選變量,如果我們不初始化,則可選變量的初始值為 nil,即,該變量沒有值。

讀取可選變量時,通過 ! 運算符進行強制解包(Forced Unwrapped),不過使用前需要先判斷變量是否有值,否則會得到一個 runtime 錯誤:

Swift
var optionalValue_1: OptionaloptionalValue_1 = 5
if optionalValue_1 != nil {
    print(optionalValue_1!) // "5"
} else {
    print("optionalValue_1 has no value")
}
var optionalValue_2: Int?
// fatal error: unexpectedly found nil while unwrapping an Optional value
print(optionalValue_2!)

隱式解包(Implicitly Unwrapped)

聲明了一個可選變量後,每次讀取都需要使用 ! 進行強制解包,寫起來比較麻煩,為了簡便起見,我們可以將聲明中的 ? 替換為 !,這樣,使用時系統會對可選變量進行隱式解包,就無需再添加 !:

Swift
var optionalString: String!
optionalString = "Hello, Implicitly Unwrapped"
if optionalString != nil {
    print(optionalString) // "Hello, Implicitly Unwrapped"
} else {
    print("optionalString has no value")
}

如此用起來方便多了,跟 Objective-C 也類似了,可是,這看上去和一個非可選變量的使用完全一樣,很容易讓人誤解為是一個正常變量,一定有值,不需要對其檢查就可以使用,這不是有違 Swift 的安全的原則嗎?為什麼以安全著稱的 Swift 會加入隱式解包並允許這種危險的寫法呢?王巍(@onevcat)在其《100個Swift開發必備Tip》一書中隱式解包 OPTIONAL章節進行了探討,以下摘錄片段:

一切都是歷史的錯。因為 Objective-C 中 Cocoa 的所有類型變量都可以指向 nil 的,有一部分 Cocoa 的 API 中在參數或者返回時即使被聲明為具體的類型,但是還是有可能在某些特定情況下是 nil,而同時也有另一部分 API 永遠不會接收或者返回 nil。在 Objective-C 時,這兩種情況並沒有被加以區別,因為 Objective-C 裡向 nil 發送消息並不會有什麼不良影響。在將 Cocoa API 從 Objective-C 轉為 Swift 的 module 聲明的自動化工具裡,是無法判定是否存在 nil 的可能的,因此也無法決定哪些類型應該是實際的類型,而哪些類型應該聲明為 Optional。

在這種自動化轉換中,最簡單粗暴的應對方式是全部轉為 Optional,然後讓使用者通過 Optional Binding 來判斷並使用。雖然這是最安全的方式,但對使用者來說是一件非常麻煩的事情,我猜不會有人喜歡每次用個 API 就在 Optional 和普通類型之間轉來轉去。這時候,隱式解包的 Optional 就作為一個妥協方案出現了。使用隱式解包 Optional 的最大好處是對於那些我們能確認的 API 來說,我們可直接進行屬性訪問和方法調用,會很方便。但是需要牢記在心的是,隱式解包不意味著 “這個變量不會是 nil,你可以放心使用” 這種暗示,只能說 Swift 通過這個特性給了我們一種簡便但是危險的使用方式罷了。

王巍(@onevcat)隱式解包 OPTIONAL

總之,只要我們在使用時堅持對可選變量進行“為空判斷”,即可安全的使用隱式解包特性。

可選綁定(Optional Binding)

可選綁定(Optional Binding)是用來判斷可選類型是否包含值,如果包含就把值賦給一個臨時常量或者變量,通常用於 if 和 while 語句中:

Swift
var optionalString: String!
optionalString = "Hello, Optional Binding"
if let tempString = optionalString {
    print(tempString) // "Hello, Optional Binding"
} else {
    print("optionalString has no value")
}

Optional 源碼解析

在掌握了 Optional 的基本使用方法之後,我們通過 Swift 源碼來了解一下 Optional 的實現細節。為了方便理解,我將分定義、解包、運算符和擴展方法 4 個部分進行講述。

Optional 定義

打開 Optional 源代碼(路徑:swift/stdlib/public/core/Optional.swift),或通過 Xcode 查看 Optional 定義,可以看到 Optional 其實是一個 enum,具體代碼如下(刪除注釋):

Optional.swift
public enum Optional : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
    
    public init(_ some: Wrapped)
    public func map(_ transform: (Wrapped) throws -> U) rethrows -> U?
    public func flatMap(_ transform: (Wrapped) throws -> U?) rethrows -> U?
    public init(nilLiteral: ())
    public var unsafelyUnwrapped: Wrapped { get }
}

通過定義可以得到以下信息:

  • 包含 none 和 some(Wrapped) 兩個 case,分別代表可選類型“沒有值”和“有值”兩種情況

既然是枚舉類型,那麼我們就可以通過判斷 case 是否匹配來讀取可選變量的值,代碼如下:

Swift
var optionalValue: String?
// 方式 1
switch optionalValue {
case .none:
    print("optionalString has no value")
default:
    print(optionalValue!)
}
// 方式 2
if optionalValue == .none {
    print("optionalString has no value")
} else {
    print(optionalValue!)
}
// 方式 3,推薦
if optionalValue == nil {
    print("optionalString has no value")
} else {
    print(optionalValue!)
}

以上三種方式效果等價,不過官方注釋裡說 In code, the absence of a value is typically written using the `nil` literal rather than the explicit `.none` enumeration case,即“沒有值”時推薦使用 nil,而非 .none,因此上述方法 3 為推薦方式,也就是我們通常所用的方式。

  • 包含一個名為 init(_ some: Wrapped) 的初始化方法

init(_ some: Wrapped) 是 Optional 提供的初始化方法,可以通過這個初始化方法來創建一個有初始值的可選變量,因此以下三種方式是等效的:

Swift
var optionalValue = Optional(5)
var optionalValue: Optional = 5
var optionalValue: Int? = 5

我們來看看 init(_ some: Wrapped) 的源碼:

init(_ some: Wrapped)
public init(_ some: Wrapped) {
    self = .some(some)
}

即:將可選變量初始化為 .some(some),這就是一個 wrapped 的值,.some 可以理解為就是 Optional,因此當我們初始化後得到的就是 Optional(some),所謂的解包,其實就是將 some 從 Optional(some) 中解出來:

Swift
var optionalValue = Optional(5)
print(optionalValue) // Optional(5)
  • 包含兩個 Map 相關方法

這部分將在本文 Map 章節進行說明。

  • 實現了 protocol ExpressibleByNilLiteral 協議中的 init(nilLiteral: ()) 方法

協議 ExpressibleByNilLiteral 中包括一個 init(nilLiteral: ()) 方法,功能是使用 nil(Optional 中為:.none)進行初始化,源碼如下:

init(nilLiteral: ())
public init(nilLiteral: ()) {
    self = .none
}

這個方法是不能直接調用的,使用 var i: Index? = nil 的方式會自動調用該方法。

  • 包含一個名為 unsafelyUnwrapped 的只讀變量

這部分將在本文 Unwrapped 章節進行說明。

Unwrapped

上面講到初始化一個可選變量後得到的是一個 wrapped 的值,使用時需要進行 unwrapped,即解包。我們都知道解包只需要在可選變量後面加上 ! 即可。

那麼 ! 具體是如何工作的呢?我們從源碼(路徑:swift/stdlib/public/core/Policy.swift)中找到它的定義:

Policy.swift
// Optional unwrapping operator is built into the compiler as a part of
// postfix expression grammar.
//
// postfix operator !

原來,解包符號 ! 作為後綴表達式被放入了編譯器中,我們無法得到其具體實現,不過這並不妨礙我們理解,因為還有另外一條線索,就是前面提到的名為 unsafelyUnwrapped 的只讀變量。

在日常開發中,我們一般不會用到 unsafelyUnwrapped,但其實它與 ! 在一定程度上是等價的,我們先來看看這個變量的源代碼:

Optional.swift
public var unsafelyUnwrapped: Wrapped {
    @inline(__always)
    get {
        if let x = self {
            return x
        }
        _debugPreconditionFailure("unsafelyUnwrapped of nil optional")
    }
}

unsafelyUnwrapped 的 get 方法和我們常寫的可選綁定語法很近似,這看上去非常安全,為什麼變量名稱是 “unsafely” 呢?稍安勿躁,我們來看看這個變量的官方注釋:

Optional.swift
/// 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.

大意如下:

unsafelyUnwrapped 就是讀取可選變量所包裹的值,不檢查是否為 nil;

unsafelyUnwrapped 和 ! 提供相同的值,但在編譯優化設置為 (-O) 時,不會做任何檢查,因此,直接訪問可選變量的 unsafelyUnwrapped 會導致未知結果;

在編譯優化設置為 (-Onone) 時,unsafelyUnwrapped 和 ! 表現相同,訪問 nil 會引發 runtime 錯誤;

unsafelyUnwrapped 比調用 unsafeBitCast(_:) 方法更推薦,因為 debug 環境會進行安全檢查;

警告:unsafelyUnwrapped 以犧牲安全性來換取更好的性能,因此需要在確保非 nil 的情況下使用。

下面我們分段解讀:

對於 1、2、3,來看以下代碼:

Swift
var optionalValue = Optional(5)
print(optionalValue!) // 5
print(optionalValue.unsafelyUnwrapped) // 5

可選變量有值的情況下,兩種解包方式效果相同,再來看下面的代碼:

Swift (`-Onone`)
var optionalValue: Optionalprint(optionalValue!) // fatal error: unexpectedly found nil while unwrapping an Optional value
print(optionalValue.unsafelyUnwrapped) // fatal error: unsafelyUnwrapped of nil optional

上述代碼是在 (-Onone) 下執行的,兩種方式得到不同表述但相同涵義的錯誤,然後我們將編譯優化切換至 (-O):

Swift (`-O`)
var optionalValue: Optionalprint(optionalValue!) // EXC_BAD_INSTRUCTION
print(optionalValue.unsafelyUnwrapped) // 0

這時,使用 ! 解包出現錯誤,但 unsafelyUnwrapped 卻正常輸出了 0,而這個 0 對我們來說並不應該出現,因為可能導致程序出現無法預知的錯誤,這也就是為什麼變量名中包含一個 “unsafely” 的原因。

第 4 點中提到了一個名為 unsafeBitCast(_:) 的方法,並說明不推薦使用,為什麼呢?我們來看看這個方法的具體源碼(路徑:swift/stdlib/public/core/Builtin.swift):

Builtin.swift
/// Returns the bits of `x`, interpreted as having type `U`.
///
/// - Warning: Breaks the guarantees of Swift's type system; use
///   with extreme care.  There's almost always a better way to do
///   anything.
///
@_transparent
public func unsafeBitCast(_ x: T, to: U.Type) -> U {
  _precondition(MemoryLayout.size == MemoryLayout.size,
    "can't unsafeBitCast between types of different sizes")
  return Builtin.reinterpretCast(x)
}

再來看一個使用的例子:

Swift
let array = NSArray(object: "obj")
let str = unsafeBitCast(CFArrayGetValueAtIndex(array, 0), to: CFString.self)
print(str) // obj

可以看出,unsafeBitCast(_:) 會將一個指針指向的內存強制按位轉換為目標的類型,並且只進行了簡單的 size 判斷。這是非常危險的操作,因為這種轉換是在 Swift 的類型管理之外進行的,因此編譯器無法確保得到的類型是否確實正確,所以使用上需要非常注意。

也是因為上述原因,unsafelyUnwrapped 比 unsafeBitCast(_:) 更安全,因為在 debug 環境下,我們能夠在不安全使用的情況下得到錯誤。

第 5 點警告也是我們日常使用可選變量時應該注意的,無論是使用 ! 或是 unsafelyUnwrapped。

運算符

通過 Xcode 進入 Optional 可以看到很多可選變量的運算符,但除了 ?? 以外均沒有注釋,下面我們還是通過源碼來了解一下吧。

??

?? 是 Swift 中一個非常有用的操作符,用來對 nil 進行快速判斷,?? 可以判斷輸入並在當左側的值是非 nil 的 Optional 值時返回其值,左側是 nil 時返回右側的值。

Swift
var optionalValue: Int?
var aValue = 5
let result = optionalValue ?? aValue
print(result) // 5
optionalValue = 1
result = optionalValue ?? aValue
print(result) // 1

在 Optional 中有兩個 ?? 方法,源碼如下(刪除注釋):

Optional.swift
public func ?? (optional: T?, defaultValue: @autoclosure () throws -> T)
    rethrows -> T {
    
    switch optional {
        case .some(let value):
            return value
        case .none:
            return try defaultValue()
    }
}
public func ?? (optional: T?, defaultValue: @autoclosure () throws -> T?)
    rethrows -> T? {
    
    switch optional {
        case .some(let value):
            return value
        case .none:
            return try defaultValue()
    }
}

我們可以得到以下信息:

兩個方法的實現完全一樣,只是第二個參數允許接收 T 和 T? 兩種類型;

?? 右側的參數 defaultValue 被封裝為了 () -> T 和 () -> T?。為什麼這樣做呢?下面再次摘錄王巍(@onevcat)在其《100個Swift開發必備Tip》一書中 @AUTOCLOSURE 和 ?? 章節部分片段進行解釋:

可能你會有疑問,為什麼這裡要使用 autoclosure,直接接受 T 作為參數並返回不行麼,為何要用 () -> T 這樣的形式包裝一遍,豈不是畫蛇添足?其實這正是 autoclosure 的一個最值得稱贊的地方。如果我們直接使用 T,那麼就意味著在 ?? 操作符真正取值之前,我們就必須准備好一個默認值傳入到這個方法中,一般來說這不會有很大問題,但是如果這個默認值是通過一系列復雜計算得到的話,可能會成為浪費 – 因為其實如果 optional 不是 nil 的話,我們實際上是完全沒有用到這個默認值,而會直接返回 optional 解包後的值的。這樣的開銷是完全可以避免的,方法就是將默認值的計算推遲到 optional 判定為 nil 之後。

王巍(@onevcat)@AUTOCLOSURE 和 ??

 

我們通過源碼也可以看出,當滿足 case .none 時,直接返回 defaultValue(),正是因為這一巧妙的用法,克服了性能上的開銷和使用上的不便,以如此優雅的方式返回默認值。

==

在 Optional 中存在三個 == 運算符的實現,用於對比可選變量的相等關系,源碼如下:

Optional.swift
public func == (lhs: T?, rhs: T?) -> Bool {
    switch (lhs, rhs) {
        case let (l?, r?):
            return l == r
        case (nil, nil):
            return true
        default:
            return false
    }
}
public func == (lhs: T?, rhs: _OptionalNilComparisonType) -> Bool {
    switch lhs {
        case .some(_):
            return false
        case .none:
            return true
    }
}
public func == (lhs: _OptionalNilComparisonType, rhs: T?) -> Bool {
    switch rhs {
        case .some(_):
            return false
        case .none:
            return true
    }
}

第一個方法中,== 被用來對比兩個遵循 protocol Equatable 的可選變量的等價關系,通過源碼可以看出,以下兩種情況會返回 true:

兩個可選變量均為 nil;

兩個可選變量均非 nil,且所包裹的值相等。

不滿足以上條件的情況均返回 false,示例代碼如下:

Swift
let group1 = [1, 2, 3, 4, 5]
let group2 = [1, 3, 5, 7, 9]
if group1.first == group2.first {
    print("Start the same.") // Start the same.
}
let left: Int? = nil
let right: Int? = nil
if left == right {
print("Equal!") // Equal!
}

另外,我們還可以將可選變量與非可選變量或常量進行比較,系統會將非可選變量或常量先 “包裹” 成一個 Optional 變量再進行上述比較:

Swift
let left: Int = 5
let right: Int? = 5
if left == right {
    print("Equal!") // Equal!
}
if 5 == right {
    print("Equal Too!") // Equal Too!
}

第二個和第三個方法我們也經常用到,它允許我們將可選變量與 nil 進行直接比較,與第一個方法的區別在於這裡的可選變量即使不遵循 protocol Equatable 也可以進行比較,方法實現與判斷可選變量是否有值的方式很類似。

參數中 _OptionalNilComparisonType 的定義源碼如下:

Optional.swift
public struct _OptionalNilComparisonType : ExpressibleByNilLiteral {
    /// Create an instance initialized with `nil`.
    @_transparent
    public init(nilLiteral: ()) {
    }
}

使用方法如下:

Swift
var str: Data? = nil
if str == nil {
    print("Data is nil.") // Data is nil.
}

!=

!= 用於可選變量的 “不等於” 判斷,實現方法共有三個,源碼如下:

Optional.swift
public func != (lhs: T?, rhs: T?) -> Bool {
    return !(lhs == rhs)
}
public func != (lhs: T?, rhs: _OptionalNilComparisonType) -> Bool {
    switch lhs {
        case .some(_):
            return true
        case .none:
            return false
    }
}
public func != (lhs: _OptionalNilComparisonType, rhs: T?) -> Bool {
    switch rhs {
        case .some(_):
            return true
        case .none:
            return false
    }
}

有了 == 的經驗,!= 就很好理解了,只是對 == 進行邏輯取反,這裡不再進行贅述。

~=

在 Optional 中,還有一個 ~= 運算符,源碼為:

Optional.swift
public func ~= (lhs: _OptionalNilComparisonType, rhs: T?) -> Bool {
    switch rhs {
        case .some(_):
            return false
        case .none:
            return true
    }
}

奇怪,這個方法的實現與 == 中第三個方法相同,為什麼要多加一個 ~= 呢?

~= 被稱為模式匹配運算符(Pattern Matching),在可選變量的判斷上與 == 確實有相同的功效,但是在使用上官方給出如下解釋:

Optional.swift
/// - Note: To test whether an instance is `nil` in an `if` statement, use the
///   equal-to operator (`==`) instead of the pattern-matching operator. The
///   pattern-matching operator is primarily intended to enable `case`
///   statement pattern matching.

原來,~= 主要是被用於 case 語句中的模式匹配,正常的可選變量判斷,我們仍然需要使用 ==。

Optional Extensions

在 Optional 中,還存在兩個 extension,源碼如下:

Optional.swift
extension Optional : CustomDebugStringConvertible {
    /// A textual representation of this instance, suitable for debugging.
    public var debugDescription: String {
        switch self {
            case .some(let value):
                var result = "Optional("
                    debugPrint(value, terminator: "", to: &result)
                    result += ")"
                return result
            case .none:
                return "nil"
        }
    }
}
extension Optional : CustomReflectable {
    public var customMirror: Mirror {
        switch self {
            case .some(let value):
                return Mirror(
                    self,
                    children: [ "some": value ],
                    displayStyle: .optional)
            case .none:
                return Mirror(self, children: [:], displayStyle: .optional)
        }
    }
}

debugDescription 變量輸出其實就是我們常常看到的 Optional(some),主要用於 debug:

Swift
var optionalValue: Int? = 5
print(optionalValue.debugDescription) // Optional(5)

第二個 extension 中的 customMirror 其實是創建了一個當前可選變量的 Mirror,Mirror 是 Swift 中定義的一個用於表示子結構的結構體類型(可以參考:Mirror API Reference),這裡不再展開,後續有時間我們再詳細論述。

Optional 補充

感謝您堅持閱讀到這裡,經過上述的討論,我們已經對 Optional 有了很詳細的了解,不過還有兩個相對比較重要的概念需要進行說明,就是可選鏈(Optional Chaining)和 Map,由於篇幅所限,以下僅進行簡單的討論,詳細內容大家可以查閱官方文檔或是文末提供的參考資料。

可選鏈(Optional Chaining)

對於可選鏈,官方文檔給出了如下定義:

Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil, the property, method, or subscript call returns nil. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

簡而言之,就是當我們需要通過一條鏈(鏈上各個環節都可能返回 nil)訪問某個屬性、方法和下標時,可以通過可選鏈的方式代替強制解包,以此簡化代碼。

以官方代碼為例:

Swift
class Person {
    var residence: Residence?
}
 
class Residence {
    var numberOfRooms = 1
}
let john = Person()
let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error

由於訪問鏈路徑上 residence 為 nil,因此訪問出錯,這時,我們可以通過可選鏈方式避免這樣的錯誤:

Swift

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."

通過上述方式,告知 Swift 幫我們在 residence 有值時解析出 numberOfRooms,從而避免訪問出錯,或是寫繁瑣的判斷代碼。

Map

Optional 中包括兩個 Map 相關方法,其源代碼如下:

Optional.swift
public func map(_ transform: (Wrapped) throws -> U)
    rethrows -> U? {
    switch self {
        case .some(let y):
            return .some(try transform(y))
        case .none:
            return .none
    }
}
public func flatMap(_ transform: (Wrapped) throws -> U?)
    rethrows -> U? {
    switch self {
        case .some(let y):
            return try transform(y)
        case .none:
            return .none
    }
}

Optional Map 提供了一種便捷的對可選變量進行變換(transform)的方法,使用 map 方法時,傳入一個 transform 閉包,內部可以將接收到可選變量解包後的值,然後在閉包內完成“變換”,再返回。如下例:計算一個可選變量的平方:

Swift
// Sample 1
let possibleNumber: Int? = Int("42")
let possibleSquare = possibleNumber.map { $0 * $0 }
print(possibleSquare) // Optional(1746)
// Sample 2
let noNumber: Int? = nil
let noSquare = noNumber.map { $0 * $0 }
print(noSquare) // nil

Sample 1 閉包中的 $0 即可選變量解包後的值(42,非 Optional(42))。如果可選變量沒有值,那麼返回即為 nil,如 Sample 2。

那麼 flatMap 方法呢?

從聲明上看,flatMap 返回的是 U?,說明該方法可以返回 Optional;從源碼上看,flatMap 與 map 的區別在於滿足 case .some(let y) 時的返回值的不同,map 要求返回 .some(try transform(y)),說明必須是 Optional “包裹”的值類型,其實就是非 Optional 類型,而 flatMap 返回 transform(y),其實是將返回值類型交給 transform() 決定,Optional 或非 Optional 均可。

flatMap 測試例子如下:

Swift
let optionalArray: [String?] = ["A", "B", "C"];
var optionalResult = optionalArray.flatMap{ $0 }
print(optionalResult) // ["A", "B", "C"]
optionalResult = optionalArray.map{ $0 }
print(optionalResult) // error: cannot convert value of type 'String?' to closure result type 'String'

參考資料

  1. The Swift Programming Language (Source Code)

  2. 隱式解包 OPTIONAL

  3. @AUTOCLOSURE 和 ??

  4. The Swift Programming Language: Optional Chaining

  5. OPTIONAL CHAINING

  6. OPTIONAL MAP


  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved