何时在Swift中使用类而不是结构体?在Swift中最好的设计模式是基于协议的编程还是基于对象的编程?

36 浏览
0 Comments

何时在Swift中使用类而不是结构体?在Swift中最好的设计模式是基于协议的编程还是基于对象的编程?

玩弄Swift时,如果您来自Java背景,为什么要选择结构体而不是类呢?看起来它们是一样的,但是结构体提供的功能比类要少。那为什么要选择它呢?

admin 更改状态以发布 2023年5月24日
0
0 Comments

这篇回答起源于struct和class性能之间的差异。不幸的是,关于我用于测量的方法有太多争议。我将其保留在下面,但请不要对此过分解读。我认为经过这么多年,Swift社区已经很清楚struct(以及enum)由于其简单和安全性总是优先的。

如果性能对你的应用很重要,那么请自己做出测量。我仍然认为大多数情况下struct性能更优,但最好的答案正如评论中有人说的:这取决于具体情况。

=== 旧答案 ===

由于struct实例在堆栈上分配,而class实例在堆上分配,因此struct有时可以快得多。

然而,你应该根据你的独特用例自己测量并做出决策。

考虑以下示例,它演示了使用struct和class包装Int数据类型的2种策略。我使用重复了10次的值,以更好地反映实际情况,其中你有多个字段。

class Int10Class {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}
struct Int10Struct {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}
func + (x: Int10Class, y: Int10Class) -> Int10Class {
    return IntClass(x.value + y.value)
}
func + (x: Int10Struct, y: Int10Struct) -> Int10Struct {
    return IntStruct(x.value + y.value)
}

使用以下代码测量性能

// Measure Int10Class
measure("class (10 fields)") {
    var x = Int10Class(0)
    for _ in 1...10000000 {
        x = x + Int10Class(1)
    }
}
// Measure Int10Struct
measure("struct (10 fields)") {
    var y = Int10Struct(0)
    for _ in 1...10000000 {
        y = y + Int10Struct(1)
    }
}
func measure(name: String, @noescape block: () -> ()) {
    let t0 = CACurrentMediaTime()
    block()
    let dt = CACurrentMediaTime() - t0
    print("\(name) -> \(dt)")
}

代码可以在https://github.com/knguyen2708/StructVsClassPerformance找到

更新(2018年3月27日):

截至Swift 4.0,Xcode 9.2,在iPhone 6S,iOS 11.2.6上运行Release构建,Swift编译器设置为-O整个模块优化

  • class版本花费了2.06秒
  • struct版本花费了4.17e-08秒(快了5000万倍)

(我不再对多次运行取平均值,因为方差非常小,小于5%)

注意:如果不进行整个模块优化,则差异要小得多。如果有人能够指出这个标志实际上是做什么的那我会很高兴。


更新(2016年5月7日):

截至Swift 2.2.1,Xcode 7.3,在iPhone 6S,iOS 9.3.1上运行Release构建,平均运行了5次,Swift编译器设置为-O整个模块优化

  • class版本花费了2.159942142秒
  • struct版本花费了5.83E-08秒(快了3700万倍)

注意:如有人提到,在实际情况下,在struct中可能会有超过1个字段,我已添加了测试来测试具有10个字段而不是1个字段的struct和class。令人惊讶的是,结果没有太大差异。


原始结果(2014年6月1日):

(运行在具有1个字段的struct/class上,而不是10个)

截至Swift 1.2,Xcode 6.3.2,在iPhone 5S,iOS 8.3上运行Release构建,平均运行了5次

  • class版本花费了9.788332333秒
  • struct版本花费了0.010532942秒(快了900倍)

旧结果(来自未知时间)

(对于只有1个字段的struct/class进行运行测试,而非10个)

在我 MacBook Pro 上进行 Release 构建的结果如下:

  • class 版本耗时 1.10082 秒
  • struct 版本耗时 0.02324 秒(快了50倍)
0
0 Comments

根据2015年非常热门的WWDC 2015黄金议题- Swift中的面向协议编程(video, transcript),Swift提供了许多使得结构体在许多情况下比类更好的特性。

如果结构体比较小且能够被拷贝,那它们就更受欢迎了,因为复制要比多次引用同一个实例更加安全。当将变量传递给多个类和/或多线程环境时,这点尤为重要。如果你总是可以将变量的副本发送到其他地方,你就不必担心其他位置在你之下更改变量的值。

使用结构体,就不需要太担心内存泄漏或多个线程竞争访问/修改单个变量实例的问题。(对于技术方面较为敏感的人,唯一的例外是在闭包内捕获结构体时,因为此时它实际上是捕获实例的引用,除非你显式地标记它要被复制)。

类也可能变得臃肿,因为类只能继承自一个超类。这促使我们创建庞大的超类,涵盖许多只有松散相关的不同能力。使用协议尤其是协议扩展,你可以提供协议的实现,从而消除了需要类来实现这种行为的必要性。

该谈话列出了以下情况下首选类:

  • 拷贝或比较实例没有意义(例如,窗口)
  • 实例生存期与外部影响有关(例如,临时文件)
  • 实例只是“接收器”,即只写的外部状态(例如CGContext)

它暗示结构体应该是默认的,而类应该是备选项。

另一方面,The Swift Programming Language文档有些自相矛盾:

结构体实例始终通过值传递,而类实例始终通过引用传递。这意味着它们适用于不同类型的任务。在考虑项目所需的数据结构和功能时,请决定每个数据结构是应定义为类还是结构体。

作为一般指导方针,请考虑在以下一种或多种情况下创建结构体:

  • 结构体的主要目的是封装一些相对简单的数据值。
  • 当你分配或传递结构体实例时,预计将复制封装的值,而不是引用它们。
  • 结构体存储的任何属性本身都是值类型,这些值类型也应该被复制,而不是引用。
  • 结构体不需要从另一个现有类型继承属性或行为。

适用于结构体的示例:

  • 一个几何形状的大小,可能包括一个宽度属性和一个高度属性,均为Double类型。
  • 引用系列内范围的方式,可能包括一个起始属性和一个长度属性,均为Int类型。
  • 3D坐标系中的一个点,可能包括x、y和z属性,均为Double类型。

在所有其他情况下,定义一个类,并创建该类的实例以进行管理和传递引用。实际上,这意味着大多数自定义数据结构应该是类,而不是结构体。

在这里,它声称我们应该默认使用类,并仅在特定情况下使用结构体。最终,您需要了解值类型与引用类型的现实世界影响,然后才能做出有根据的决定何时使用结构体或类。此外,请记住,这些概念始终在不断发展,Swift编程语言文档是在 Protocol Oriented Programming 演讲之前编写的。

0