无锡景观服务 發表於 2023-4-7 10:50:50

Swift中的可选项Optional解包方式实现原理

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>为什么需要Optional</li><li>什么是Optional</li><li>Optional实现原理</li><li>Optional的解包方式</li><ul class="second_class_ul"><li>1. 可选项绑定(Optional Binding)</li><li>2. 强制解包(Forced Unwrapping)</li><li>3. 隐式解包(Implicitly Unwrapped Optionals)</li></ul><li>可选链(Optional Chaining)</li><ul class="second_class_ul"></ul><li>Optional 的嵌套</li><ul class="second_class_ul"></ul></ul></div><p class="maodian"></p><h2>为什么需要Optional</h2>
<div class="cros igoods"><div class="goodsin" data-img="https://img14.360buyimg.com/pop/jfs/t1/65534/36/17196/97941/6142f75bEaf535792/d3b2070ac03898db.jpg" data-name="Swift 5从零到精通iOS开发训练营" data-owner="京东自营" data-price="105.1" data-tgid="38" data-url="https://union-click.jd.com/jdc?e=&amp;p=JF8BAMoJK1olXwUFU1xdCE4TBl8IGFMXVQcGXG4ZVxNJXF9RXh5UHw0cSgYYXBcIWDoXSQVJQwYBXFxVCU8fHDZNRwYlWG13Fl8dTxd0QhFdHy1LDkNEAiE8eEcbM2gNHF4dXwMBZF5eDkwXAmoIK2sVXDZQOobrvpOysnPcsdTA1ZEyVW5dD00fCm8MH18XWA4CZF5VDHtUVypcWBhdbTYyV25tOEsnAF9WdVpGWgMLVF5fZhZAQmxNHg1AMwYHUVxeAUMTC18KGloXXzYy"></div></div>
<p>Swift中引入了可选项(Optional)的概念是为了解决在代码中对于某些变量或常量可能为nil的情况进行处理,从而减少了程序中的不确定性,使得程序更加稳定和安全。</p>
<p class="maodian"></p><h2>什么是Optional</h2>
<p>在Swift中,可选项的类型是使用?来表示的,例如String?即为一个可选的字符串类型,表示这个变量或常量可能为nil。而对于不可选项,则直接使用相应类型的名称,例如String表示一个非可选的字符串类型。</p>
<div class="jb51code"><pre class="brush:cpp;">var str: String = nil
var str1: String? = nil
</pre></div>
<p class="maodian"></p><h2>Optional实现原理</h2>
<p>Optional实际上是Swift语言中的一种枚举类型。在Swift中声明Optional类型时,编译器会自动将其转换成对应的枚举类型,例如:</p>
<div class="jb51code"><pre class="brush:cpp;">var optionalValue: Int? = 10
// 等价于:
enum Optional&lt;Int&gt; {
    case none
    case some(Int)
}
var optionalValue: Optional&lt;Int&gt; = .some(10)
</pre></div>
<p>在上面的代码中,我们声明了一个Optional类型的变量optionalValue,并将其初始化为10。实际上,编译器会自动将其转换为对应的枚举类型,即Optional枚举类型的.some(Int),其中的Int就是我们所声明的可选类型的关联值。</p>
<p>当我们在使用Optional类型的变量时,可以通过判断其枚举值是.none还是.some来确定它是否为nil。如果是.none,表示该Optional值为空;如果是.some,就可以通过访问其关联值获取具体的数值。</p>
<p>Optional的源码实现为:</p>
<div class="jb51code"><pre class="brush:cpp;">@frozen public enum Optional&lt;Wrapped&gt; : ExpressibleByNilLiteral {
        case none
        case some(Wrapped)                                                                                                                       
}
</pre></div>
<ul><li>Optioanl其实是标准库里的一个enum类型</li><li>用标准库实现语言特性的典型</li><li>Optional.none 就是nil</li><li>Optional.some 就是包装了实际的值</li><li>泛型属性 unsafelyUnwrapped</li><li>理论上我们可以直接调用unsafelyUnwrapped获取可选项的值</li></ul>
<p class="maodian"></p><h2>Optional的解包方式</h2>
<p class="maodian"></p><h3>1. 可选项绑定(Optional Binding)</h3>
<p>使用 if let 或者 guard let 语句来判断 Optional 变量是否有值,如果有值则解包,并将其赋值给一个非可选类型的变量。</p>
<div class="jb51code"><pre class="brush:cpp;">var optionalValue: Int? = 10
// 可选项绑定
if let value = optionalValue {
    print("Optional value is \(value)")
} else {
    print("Optional value is nil")
}
</pre></div>
<p>可选项绑定语句有两个分支:if分支和else分支。如果 optionalValue 有值,if 分支就会被执行,unwrappedValue 就会被赋值为 optionalValue 的值。否则,执行 else 分支。</p>
<p class="maodian"></p><h3>2. 强制解包(Forced Unwrapping)</h3>
<p>使用!来获取一个不存在的可选值会导致运行错误,在使用!强制展开之前必须保证可选项中包含一个非nil的值</p>
<div class="jb51code"><pre class="brush:cpp;">var optionalValue: Int? = 10
let nonOptionalValue = optionalValue!// 解包optionalValue值
print(nonOptionalValue)                // 输出:10
</pre></div>
<p>需要注意的是,如果 Optional 类型的值为 nil,使用强制解包方式解包时,会导致运行时错误 (Runtime Error)。</p>
<p class="maodian"></p><h3>3. 隐式解包(Implicitly Unwrapped Optionals)</h3>
<p>在定义 Optional 类型变量时使用 ! 操作符,标明该变量可以被隐式解包。用于在一些情况下,我们可以确定该 Optional 变量绑定后不会为 nil,可以快捷的解包而不用每次都使用 ! 或者 if let 进行解包。</p>
<div class="jb51code"><pre class="brush:cpp;">var optionalValue: Int! = 10
let nonOptionalValue = optionalValue // 隐式解包
print(nonOptionalValue) // 输出:10
</pre></div>
<p>需要注意的是,隐式解包的 Optional 如果 nil 的话,会导致 runtime error,所以使用隐式解包 Optional 需要确保其一直有值,否则还是需要检查其非 nil 后再操作。</p>
<p>总的来说,我们应该尽量避免使用强制解包,而是通过可选项绑定来处理 Optional 类型的值,在需要使用隐式解包的情况下,也要确保其可靠性和稳定性,尽量减少出现运行时错误的概率。</p>
<p class="maodian"></p><h2>可选链(Optional Chaining)</h2>
<p>是一种在 Optional 类型值上进行操作的方式,可以将多个 Optional 值的处理放在一起,并在任何一个 Optional 值为 nil 的时刻停止处理。</p>
<p>通过在 Optional 类型值后面跟上问号 ?,我们就可以使用可选链来访问该 Optional 对象的属性和方法。</p>
<div class="jb51code"><pre class="brush:cpp;">class Person {
    var name: String
    var father: Person?
    init(name: String, father: Person?) {
      self.name = name
      self.father = father
    }
}
let father = Person(name: "Father", father: nil)
let son = Person(name: "Son", father: father)
// 可选链调用属性
if let fatherName = son.father?.name {
    print("Father's name is \(fatherName)") // 输出:Father's name is Father
} else {
    print("Son without father")
}
// 可选链调用方法
if let count = son.father?.name.count {
    print("Father's name has \(count) characters") // 输出:Father's name has 6 characters
} else {
    print("Son without father")
}
</pre></div>
<p>在上面的代码中,我们定义了一个 Person 类,并初始化了一个包含父亲(father)的儿子(son)对象。其中,父亲对象的father属性为nil。我们使用问号 ? 来标记 father 对象为 Optional 类型,以避免访问 nil 对象时的运行时错误。</p>
<p>需要注意的是,如果一个 Optional 类型的属性通过可选链调用后,返回值不是 Optional 类型,那么在可选链调用后,就不再需要加问号 ? 标记其为 Optional 类型了。</p>
<div class="jb51code"><pre class="brush:cpp;">class Person {
    var name: String
    var age: Int?
    init(name: String, age: Int?) {
      self.name = name
      self.age = age
    }
    func printInfo() {
      print("\(name), \(age ?? 0) years old")
    }
}
let person = Person(name: "Tom", age: nil)
// 可选链调用方法后,返回值不再是 Optional 类型
let succeed = person.printInfo() // 输出:Tom, 0 years old
</pre></div>
<p>在上面的代码中,我们定义了一个 Person 类,并初始化了一个包含年龄(age)的人(person)对象。在可选链调用对象的方法&mdash;&mdash;printInfo() 方法后,因为该方法返回值不是 Optional 类型,所以 returnedValue 就不再需要加问号 ? 标记其为 Optional 类型了。</p>
<p class="maodian"></p><h2>Optional 的嵌套</h2>
<p>将一个 Optional 类型的值作为另一个 Optional 类型的值的成员,形成嵌套的 Optional 类型。</p>
<div class="jb51code"><pre class="brush:cpp;">var optionalValue: Int? = 10
var nestedOptionalValue: Int?? = optionalValue
</pre></div>
<p>在上面的代码中,我们定义了一个 Optional 类型的变量 optionalValue,并将其赋值为整型变量 10。然后,我们将 optionalValue 赋值给了另一个 Optional 类型的变量 nestedOptionalValue,形成了一个嵌套的 Optional 类型。</p>
<p>在处理嵌套的 Optional 类型时,我们需要特别小心,因为它们的使用很容易造成逻辑上的混淆和错误。为了解决这个问题,我们可以使用 Optional Binding 或者 ?? 操作符(空合并运算符)来降低 Optional 嵌套的复杂度。</p>
<div class="jb51code"><pre class="brush:cpp;">var optionalValue: Int? = 10
var nestedOptionalValue: Int?? = optionalValue
// 双重可选项绑定
if let nestedValue = nestedOptionalValue, let value = nestedValue {
    print(value) // 输出:10
} else {
    print("Optional is nil")
}
// 空合并运算符
let nonOptionalValue = nestedOptionalValue ?? 0
print(nonOptionalValue) // 输出:Optional(10)
</pre></div>
<p>在上面的代码中,我们使用了双重可选项绑定来判断 nestedOptionalValue 是否可绑定,以及其嵌套的 Optional 值是否可绑定,并将该值赋值给变量 value,以避免 Optional 值的嵌套。另外,我们还可以使用 ?? 操作符(空合并运算符)来对嵌套的 Optional 值进行默认取值的操作。</p>
<p>需要注意的是,虽然我们可以使用 ?? 操作符来降低 Optional 值的嵌套,但在具体的实际应用中,我们应该在设计时尽量避免 Optional 值的嵌套,以便代码的可读性和维护性。如果对于某个变量来说,它的值可能为空,我们可以考虑使用默认值或者定义一个默认值的 Optional 值来代替嵌套的 Optional 类型。</p>
<p>学习 Swift,勿忘初心,方得始终。但要陷入困境时,也不要忘了最初的梦想和时代所需要的技能。</p>
<p>以上就是Swift中的可选项Optional解包方式实现原理的详细内容,更多关于Swift可选项Optional的资料请关注琼殿技术社区其它相关文章!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>Java Optional用法面试题精讲</li><li>JDK8新出Optional类的方法探索与思考分析</li><li>JavaScript 新提案optional chaining可选链属性原理详解</li><li>Maven&nbsp;Optional依赖属性的含义及妙用</li><li>Java8新特性Optional类及新时间日期API示例详解</li><li>Java中Optional类及orElse方法详解</li><li>Java8新特性Optional类处理空值判断回避空指针异常应用</li><li>java封装空值建议使用Optional替代null的方法示例解析</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: Swift中的可选项Optional解包方式实现原理