Swift可选值优化示例详解

 更新时间:2023年06月19日 11:46:15   作者:云音乐技术团队  
这篇文章主要为大家介绍了Swift可选值优化示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

nil 的语义

在 Objective-C 中,nil 表示空对象,它本质是一个指向 0x00000000 的指针。但对于非指针的值类型,OC 中是无法表示_没有值_这个概念的,比如 NSInteger,它可以是 0,也可以是其他任何值,但就是不存在_没有值_。

Swift 作为一种强类型的语言,它从一开始就引入了_没有值_这个概念,虽然还是用 nil 关键字,但实际语义上有所不同。比如 Int?,它可以是 nil,也可以是 00 是一个具体的值,而 nil 不是。然而,计算机作为一个二进制的机器,它内存中保存的非 0 即 1,如何表示_没有值_呢?换句话说,nil 在内存中究竟是什么?我们可以通过简单的代码找出它在内存中的真相。

nil 在内存中的表示

/// 以下方法取 value 的地址,并从地址处向后取它在内存中的大小 size 个字节,转为对应的数组
func bytes<T>(of value: T) -> [UInt8] {
    var value = value
    let size = MemoryLayout<T>.size
    return withUnsafePointer(to: &value, {
        $0.withMemoryRebound(
            to: UInt8.self,
            capacity: size,
            {
                Array(UnsafeBufferPointer(
                    start: $0, count: size))
            })
    })
}
var int: Int? = 0
bytes(of: int)    // [0, 0, 0, 0, 0, 0, 0, 0, 0]
int = nil
bytes(of: int)    // [0, 0, 0, 0, 0, 0, 0, 0, 1]

从上面我们可以得知,可选的 Int? 类型比普通 Int 类型多占一个字节,用来表示是不是 没有值。如果这样的话,在 struct 或 class 中用可选类型岂不是会浪费较多内存空间?因为内存对齐的缘故,多一个字节,就要浪费剩下的 7 字节,比如:

struct N {
    var b: Int? = 2
    var a: Int? = 3
}
var n = N()
bytes(of: n)    // [2, 0, 0, 0, 0, 0, 0, 0, 0, 76, 68, 3, 1, 0, 0, 0, 
                //  3, 0, 0, 0, 0, 0, 0, 0, 0]

以上原本可以用 16 字节表示的结构体,实际上占了 25 字节(考虑结尾处内存对齐,其实占了 32 字节)。我们在实际开发中,可能会在 class 中声明大量的可选字段,如果都这样的话,那内存使用率也太低了,有优化手段吗?

答案是有的,而且 Swift 编译器已经默默帮我们做了。

nil 的优化

Bool

Bool 类型理论上只用 0 1 两个值,一个 bit 即可,但它却占了一整个 byte ,剩下的几个 bit 是可以用来区分是否有值的。

var b: Bool? = false
bytes(of: b) // [0]
b = true
bytes(of: b) // [1]
b = nil
bytes(of: b) // [2]

从以上结果得知,Swift 用 2 表示 Bool? 的_没有值_,所以没有内存浪费。这样也使得 Bool? 不再是两态的开关,而是一个三态的开关。于是经常在代码中看到看起来比较蠢的写法:

var value: Bool?
if value == true {
}

因为一般来说是不建议 Bool 值与 true 判断等的,它本身已经是 Bool 了。而在 Swift 中又用起来是那么自然……

String

String 类型不同于 Int 这种——0 也是合法值,String 的内存值为 0 是可以表示_没有值_的,所以它也没有内存浪费

String 在 Swift 中是一个结构体,无论字符串多长,String 变量本身只占 16 字节,短的字符串通过类似 OC 中 Tagged Pointer 的技术直接存在指针中,长的字符串需要指向堆内存地址。

Class

Class 类型同 OC 中的一样,是指针类型,空指针可以表示_没有值_,没有内存浪费。

class MyObject {
    var b: Int? = 2
    var a: Int? = 3
}
var o: MyObject? = .init()
bytes(of: o)  // [160, 142, 188, 2, 0, 96, 0, 0]
o = nil
bytes(of: o)  // [0, 0, 0, 0, 0, 0, 0, 0]

无论 Class 中有多少成员变量,Class 变量本身(即指向它的指针)只占 8 字节(64位系统中)。

Enum

枚举类型一般是有限的,最终总可以找到一个不在枚举范围内的值表示 _没有值_,也可以没有内存浪费。

enum Edge {
    case left
    case right
    case top
    case bottom
}
var e: Edge? = .left
bytes(of: e)  // [0]
e = .bottom
bytes(of: e)  // [3]
e = nil
bytes(of: e)  // [4],用越界值表示 nil,没有值

当然并不是所有 Enum 类型都能这样,带关联值的就可能不行。

结语

综上所述,Swift 编译器会尽可能地优化可选值的内存占用,日常开发并不需要太多关心,但是部分情况仍要求开发者尽量少使用可选值,如结构体中连续几个可选 Int 的情况,如果 0 也能满足代码逻辑,就使用非可选值,并用 0 初始化它吧!

// 浪费的内存比较可观
struct My {
   var a: Int?
   var b: Int?
   var c: Int?
   var d: Int?
}

以上就是Swift可选值优化示例详解的详细内容,更多关于Swift可选值优化的资料请关注脚本之家其它相关文章!

相关文章

  • 详解Swift的内存管理

    详解Swift的内存管理

    这篇文章主要介绍了Swift的内存管理,对内存管理感兴趣的同学,可以参考下
    2021-04-04
  • 判断 ScrollView List 是否正在滚动详解

    判断 ScrollView List 是否正在滚动详解

    这篇文章主要为大家介绍了判断 ScrollView、List 是否正在滚动示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Swift如何调用Objective-C的可变参数函数详解

    Swift如何调用Objective-C的可变参数函数详解

    这篇文章主要给大家介绍了关于Swift如何调用Objective-C的可变参数函数的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用swift具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-03-03
  • 如何在Swift 中使用 async let 并发运行后台任务

    如何在Swift 中使用 async let 并发运行后台任务

    Swift 异步编程是一种编写允许某些任务并发运行而不是按顺序运行的代码的方法,这篇文章主要介绍了在Swift中使用async let并发运行后台任务,需要的朋友可以参考下
    2023-06-06
  • Swift中添加双击手势识别器

    Swift中添加双击手势识别器

    在这次IOS应用开发教程中,我们打算实现手势识别。正如你所知道的,IOS支持大量的手势操作,它们能提供了很好的应用控制和出色用户体验。
    2019-08-08
  • Swift Error重构的基础示例详解

    Swift Error重构的基础示例详解

    这篇文章主要为大家介绍了Swift Error基础错误处理的方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Swift教程之枚举类型详解

    Swift教程之枚举类型详解

    这篇文章主要介绍了Swift教程之枚举类型详解,本文讲解了枚举语法、匹配枚举值与switch语句、关联值、原始值等内容,需要的朋友可以参考下
    2015-01-01
  • 在Swift中使用Objective-C编写类、继承Objective-C类

    在Swift中使用Objective-C编写类、继承Objective-C类

    这篇文章主要介绍了在Swift中使用Objective-C编写类、继承Objective-C类等操作方法介绍,需要的朋友可以参考下
    2014-07-07
  • LeetCode 刷题 Swift 两个数组的交集

    LeetCode 刷题 Swift 两个数组的交集

    这篇文章主要为大家介绍了LeetCode 刷题 Swift 两个数组的交集示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Swift算法之栈和队列的实现方法示例

    Swift算法之栈和队列的实现方法示例

    Swift语言中没有内设的栈和队列,很多扩展库中使用Generic Type来实现栈或是队列。下面这篇文章就来给大家详细介绍了Swift算法之栈和队列的实现方法,需要的朋友可以参考学习,下面来一起看看吧。
    2017-03-03

最新评论