Swift之Optional

为什么需要Optional

  1. Objective-C里面的nil是无类型指针,不同那个类型无法区别。
  2. Objective-C
  3. Objective-C中的所有对性都可以设置为nil,运行的时候不能直接判定当前对象是否为空。
  4. Objective-C里面的nil只能使用在对象上,而其他类型则需要其他值表示,比如NSNotFound。也就是说值缺失的情况有了不同的表达方式。

Optional

  1. 变量类型后面加上?表示可选类型,有两层含义:

    1. 这里有值,等于x
    2. 这里根本没有值,即值为空
  2. 可以给可选变量设置为nil表示值为空

    1. 在Objective-C中,nil表示一个纸箱不存在对象的指针
    2. 在Swift中,nil不是指针,他是值缺失的一中特殊类型,任何类型的可选项 都可以设置为nil而不仅仅是对象类型

可选项的使用

可选项无法直接使用,如果要使用可选项可以通过以下四种方式:

强制解包

使用!号强制解包,如果可选项有值,则代码可以正常运行;如果可选项值缺失(即为nil)则运行时则会发生错误:

 Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).

 Fatal error: Unexpectedly found nil while unwrapping an Optional value
var str: String? = "abc"
print(str!.count)

可选绑定

  1. 可以使用可选项绑定判断可选项是否包含值,如果包含值就赋给一个临时的变量或者常量。
  2. 可选绑定可以与if或者while一起使用检查可选项内部的值,并付给常量或者变量。
  3. 同一个if语句可以进行多个可选项绑定,如果其中一个为nil或者Bool类型值为false,if表达式值即为false。
var name: String? = "Mary"
var age: Int? = 20

if let name = name, let age = age {
    print("\(name) is \(age) years old.")
}

var weather: String? = nil
if let weather = weather {
    print("It is a \(weather) day.")
} else {
    print("Today may be a sunny day.")
}
Mary is 20 years old.
Today may be a sunny day.

可选绑定有三种方式:

  1. if: 大括号内使用解包之后的值
  2. swich
  3. guard:解包之后直接使用,不许用大括号包裹;多个需要解包的值时,避免金字塔灾难,推荐使用此方式。
let str: Optional<String> = "abc" //equals to : let str1: String? = "abc"
if let str1 = str {
    print(str1.count)
}
if str != nil {
    print(str.unsafelyUnwrapped.count)
}

switch str {
case .none:
    print("Empty string!")
case .some(let str2):
    print("str = \(str2)  count = \(str2.count)")
}

enum UnpackError : Error {
  case emptyString(message: String)
}
guard let str3 = str else {
    throw  UnpackError.emptyString(message: "Empty String")
}
print("str = \(str3)  count = \(str3.count)")

隐式展开

  1. 有些可选项在设定值之后,就会一直拥有,这种情况下就没有必要每次使用的时候都去检查。
  2. 使用方法:类型后面用!号而非?号
  3. 主要用于Swift类的初始化过程中
var str: String! = "abc"
print(str.count)

可选链

  1. 可选项后面加上?号
  2. 如果可选项有值返回可选结果,否则返回nil。
var str: String? = "abc"
print("\(String(describing: str?.count))")

注意打印的结果:

Optional(3)

Optional实现探究

  1. Optional并不是语言内置特性而是标准库里面的一个enum类型。
  2. Optional是用标准库实现语言特性的典型。
  3. 以下是optional的部分源代码:
    1. Optional.none--->nil
    2. Optional.some--->包装了实际值
    3. 可以用Optional枚举类型定义可选项
    4. Optional.unsafelyUnwrapped来获取可选项的值。
/// A type that represents either a wrapped value or `nil`, the absence of a
/// value.
///
/// You use the `Optional` type whenever you use optional values, even if you
/// never type the word `Optional`. Swift's type system usually shows the
/// wrapped type's name with a trailing question mark (`?`) instead of showing
/// the full type name. For example, if a variable has the type `Int?`, that's
/// just another way of writing `Optional<Int>`. The shortened form is
/// preferred for ease of reading and writing code.
///
/// The types of `shortForm` and `longForm` in the following code sample are
/// the same:
///
///     let shortForm: Int? = Int("42")
///     let longForm: Optional<Int> = Int("42")
///
/// The `Optional` type is an enumeration with two cases. `Optional.none` is
/// equivalent to the `nil` literal. `Optional.some(Wrapped)` stores a wrapped
/// value. For example:
///
///     let number: Int? = Optional.some(42)
///     let noNumber: Int? = Optional.none
///     print(noNumber == nil)
///     // Prints "true"
///
/// You must unwrap the value of an `Optional` instance before you can use it
/// in many contexts. Because Swift provides several ways to safely unwrap
/// optional values, you can choose the one that helps you write clear,
/// concise code.
///
/// The following examples use this dictionary of image names and file paths:
///
///     let imagePaths = ["star": "/glyphs/star.png",
///                       "portrait": "/images/content/portrait.jpg",
///                       "spacer": "/images/shared/spacer.gif"]
///
/// Getting a dictionary's value using a key returns an optional value, so
/// `imagePaths["star"]` has type `Optional<String>` or, written in the
/// preferred manner, `String?`.
///
/// Optional Binding
/// ----------------
///
/// To conditionally bind the wrapped value of an `Optional` instance to a new
/// variable, use one of the optional binding control structures, including
/// `if let`, `guard let`, and `switch`.
///
///     if let starPath = imagePaths["star"] {
///         print("The star image is at '\(starPath)'")
///     } else {
///         print("Couldn't find the star image")
///     }
///     // Prints "The star image is at '/glyphs/star.png'"
///
/// Optional Chaining
/// -----------------
///
/// To safely access the properties and methods of a wrapped instance, use the
/// postfix optional chaining operator (postfix `?`). The following example uses
/// optional chaining to access the `hasSuffix(_:)` method on a `String?`
/// instance.
///
///     if imagePaths["star"]?.hasSuffix(".png") == true {
///         print("The star image is in PNG format")
///     }
///     // Prints "The star image is in PNG format"
///
/// Using the Nil-Coalescing Operator
/// ---------------------------------
///
/// Use the nil-coalescing operator (`??`) to supply a default value in case
/// the `Optional` instance is `nil`. Here a default path is supplied for an
/// image that is missing from `imagePaths`.
///
///     let defaultImagePath = "/images/default.png"
///     let heartPath = imagePaths["heart"] ?? defaultImagePath
///     print(heartPath)
///     // Prints "/images/default.png"
///
/// The `??` operator also works with another `Optional` instance on the
/// right-hand side. As a result, you can chain multiple `??` operators
/// together.
///
///     let shapePath = imagePaths["cir"] ?? imagePaths["squ"] ?? defaultImagePath
///     print(shapePath)
///     // Prints "/images/default.png"
///
/// Unconditional Unwrapping
/// ------------------------
///
/// When you're certain that an instance of `Optional` contains a value, you
/// can unconditionally unwrap the value by using the forced
/// unwrap operator (postfix `!`). For example, the result of the failable `Int`
/// initializer is unconditionally unwrapped in the example below.
///
///     let number = Int("42")!
///     print(number)
///     // Prints "42"
///
/// You can also perform unconditional optional chaining by using the postfix
/// `!` operator.
///
///     let isPNG = imagePaths["star"]!.hasSuffix(".png")
///     print(isPNG)
///     // Prints "true"
///
/// Unconditionally unwrapping a `nil` instance with `!` triggers a runtime
/// error.
public enum Optional<Wrapped> : ExpressibleByNilLiteral {

    /// The absence of a value.
    ///
    /// In code, the absence of a value is typically written using the `nil`
    /// literal rather than the explicit `.none` enumeration case.
    case none

    /// The presence of a value, stored as `Wrapped`.
    case some(Wrapped)

    /// Creates an instance that stores the given value.
    public init(_ some: Wrapped)

    /// Creates an instance initialized with `nil`.
    ///
    /// Do not call this initializer directly. It is used by the compiler when you
    /// initialize an `Optional` instance with a `nil` literal. For example:
    ///
    ///     var i: Index? = nil
    ///
    /// In this example, the assignment to the `i` variable calls this
    /// initializer behind the scenes.
    public init(nilLiteral: ())

    /// 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 { get }
}

extension Optional : Equatable where Wrapped : Equatable {

    /// Returns a Boolean value indicating whether two optional instances are
    /// equal.
    ///
    /// Use this equal-to operator (`==`) to compare any two optional instances of
    /// a type that conforms to the `Equatable` protocol. The comparison returns
    /// `true` if both arguments are `nil` or if the two arguments wrap values
    /// that are equal. Conversely, the comparison returns `false` if only one of
    /// the arguments is `nil` or if the two arguments wrap values that are not
    /// equal.
    ///
    ///     let group1 = [1, 2, 3, 4, 5]
    ///     let group2 = [1, 3, 5, 7, 9]
    ///     if group1.first == group2.first {
    ///         print("The two groups start the same.")
    ///     }
    ///     // Prints "The two groups start the same."
    ///
    /// You can also use this operator to compare a non-optional value to an
    /// optional that wraps the same type. The non-optional value is wrapped as an
    /// optional before the comparison is made. In the following example, the
    /// `numberToMatch` constant is wrapped as an optional before comparing to the
    /// optional `numberFromString`:
    ///
    ///     let numberToFind: Int = 23
    ///     let numberFromString: Int? = Int("23")      // Optional(23)
    ///     if numberToFind == numberFromString {
    ///         print("It's a match!")
    ///     }
    ///     // Prints "It's a match!"
    ///
    /// An instance that is expressed as a literal can also be used with this
    /// operator. In the next example, an integer literal is compared with the
    /// optional integer `numberFromString`. The literal `23` is inferred as an
    /// `Int` instance and then wrapped as an optional before the comparison is
    /// performed.
    ///
    ///     if 23 == numberFromString {
    ///         print("It's a match!")
    ///     }
    ///     // Prints "It's a match!"
    ///
    /// - Parameters:
    ///   - lhs: An optional value to compare.
    ///   - rhs: Another optional value to compare.
    @inlinable public static func == (lhs: Wrapped?, rhs: Wrapped?) -> Bool
}

/// Performs a nil-coalescing operation, returning the wrapped value of an
/// `Optional` instance or a default value.
///
/// A nil-coalescing operation unwraps the left-hand side if it has a value, or
/// it returns the right-hand side as a default. The result of this operation
/// will have the non-optional type of the left-hand side's `Wrapped` type.
///
/// This operator uses short-circuit evaluation: `optional` is checked first,
/// and `defaultValue` is evaluated only if `optional` is `nil`. For example:
///
///     func getDefault() -> Int {
///         print("Calculating default...")
///         return 42
///     }
///
///     let goodNumber = Int("100") ?? getDefault()
///     // goodNumber == 100
///
///     let notSoGoodNumber = Int("invalid-input") ?? getDefault()
///     // Prints "Calculating default..."
///     // notSoGoodNumber == 42
///
/// In this example, `goodNumber` is assigned a value of `100` because
/// `Int("100")` succeeded in returning a non-`nil` result. When
/// `notSoGoodNumber` is initialized, `Int("invalid-input")` fails and returns
/// `nil`, and so the `getDefault()` method is called to supply a default
/// value.
///
/// - Parameters:
///   - optional: An optional value.
///   - defaultValue: A value to use as a default. `defaultValue` is the same
///     type as the `Wrapped` type of `optional`.
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T

/// Performs a nil-coalescing operation, returning the wrapped value of an
/// `Optional` instance or a default `Optional` value.
///
/// A nil-coalescing operation unwraps the left-hand side if it has a value, or
/// returns the right-hand side as a default. The result of this operation
/// will be the same type as its arguments.
///
/// This operator uses short-circuit evaluation: `optional` is checked first,
/// and `defaultValue` is evaluated only if `optional` is `nil`. For example:
///
///     let goodNumber = Int("100") ?? Int("42")
///     print(goodNumber)
///     // Prints "Optional(100)"
///
///     let notSoGoodNumber = Int("invalid-input") ?? Int("42")
///     print(notSoGoodNumber)
///     // Prints "Optional(42)"
///
/// In this example, `goodNumber` is assigned a value of `100` because
/// `Int("100")` succeeds in returning a non-`nil` result. When
/// `notSoGoodNumber` is initialized, `Int("invalid-input")` fails and returns
/// `nil`, and so `Int("42")` is called to supply a default value.
///
/// Because the result of this nil-coalescing operation is itself an optional
/// value, you can chain default values by using `??` multiple times. The
/// first optional value that isn't `nil` stops the chain and becomes the
/// result of the whole expression. The next example tries to find the correct
/// text for a greeting in two separate dictionaries before falling back to a
/// static default.
///
///     let greeting = userPrefs[greetingKey] ??
///         defaults[greetingKey] ?? "Greetings!"
///
/// If `userPrefs[greetingKey]` has a value, that value is assigned to
/// `greeting`. If not, any value in `defaults[greetingKey]` will succeed, and
/// if not that, `greeting` will be set to the non-optional default value,
/// `"Greetings!"`.
///
/// - Parameters:
///   - optional: An optional value.
///   - defaultValue: A value to use as a default. `defaultValue` and
///     `optional` have the same type.
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,745评论 2 9
  • 前言 Swift的可选类型可以用来表示可能缺失或是计算失败的值。今天和大家分享的笔记是如何有效地利用可选类型,以及...
    请叫我小陈陈阅读 466评论 1 1
  • Optional 是 Swift 的一大特色,也是 Swift 初学者最容易困惑的问题定义变量时,如果指定是可选的...
    wustzhy阅读 391评论 0 0
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 应用的新语言。然而,如果你有 C 或...
    XLsn0w阅读 922评论 2 1