玩Swift,来自Java背景,为什么要选择Struct而不是Class?看起来它们是一样的东西,只不过Struct提供的功能更少。那为什么选择它呢?


当前回答

根据2015年非常流行的WWDC演讲,Swift中面向协议的编程(视频,文本),Swift提供了许多特性,使得结构在很多情况下比类更好。

如果结构相对较小且可复制,则更可取,因为复制比类中对同一个实例有多个引用要安全得多。当将一个变量传递给多个类和/或多线程环境时,这一点尤其重要。如果你总是可以将变量的副本发送到其他地方,你就不必担心其他地方会改变你下面变量的值。

使用Structs,不太需要担心内存泄漏或多个线程竞相访问/修改变量的单个实例。(对于更有技术头脑的人来说,例外情况是在闭包内捕获结构时,因为它实际上是在捕获实例的引用,除非您显式地将其标记为要复制)。

类也可能变得臃肿,因为一个类只能继承一个超类。这鼓励我们创建巨大的超类,其中包含许多不同的能力,而这些能力之间只有松散的关联。使用协议,特别是使用可以为协议提供实现的协议扩展,允许您消除实现此类行为所需的类。

演讲列出了优先使用类的这些场景:

复制或比较实例没有意义(例如,Window) 实例生命周期与外部效果(例如,TemporaryFile)相关联。 实例只是“接收器”——只写外部状态的管道(例如cgcontext)

这意味着结构应该是默认的,而类应该是备用的。

另一方面,Swift编程语言文档有些矛盾:

Structure instances are always passed by value, and class instances are always passed by reference. This means that they are suited to different kinds of tasks. As you consider the data constructs and functionality that you need for a project, decide whether each data construct should be defined as a class or as a structure. As a general guideline, consider creating a structure when one or more of these conditions apply: The structure’s primary purpose is to encapsulate a few relatively simple data values. It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an instance of that structure. Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced. The structure does not need to inherit properties or behavior from another existing type. Examples of good candidates for structures include: The size of a geometric shape, perhaps encapsulating a width property and a height property, both of type Double. A way to refer to ranges within a series, perhaps encapsulating a start property and a length property, both of type Int. A point in a 3D coordinate system, perhaps encapsulating x, y and z properties, each of type Double. In all other cases, define a class, and create instances of that class to be managed and passed by reference. In practice, this means that most custom data constructs should be classes, not structures.

在这里,它声称我们应该只在特定的情况下默认使用类和使用结构。最后,您需要了解值类型与引用类型的实际含义,然后才能就何时使用结构或类做出明智的决定。此外,请记住,这些概念一直在发展,Swift编程语言文档是在面向协议编程演讲之前编写的。

其他回答

对于类,您获得继承并通过引用传递,而结构则没有继承并通过值传递。

有很多关于Swift的WWDC会议,其中一个会议详细回答了这个问题。确保你看了这些,因为它会让你更快地跟上语言指南或iBook。

以下是一些值得考虑的其他原因:

struct有一个自动初始化式,你根本不需要在代码中维护它。 struct MorphProperty { var类型:MorphPropertyValueType var键:字符串 var值:AnyObject enum MorphPropertyValueType { case字符串,Int, Double } } var m = MorphProperty(类型:.Int,键:“什么”,值:“blah”)

要在类中得到这个,你必须添加初始化式,并维护初始化式…

Basic collection types like Array are structs. The more you use them in your own code, the more you will get used to passing by value as opposed to reference. For instance: func removeLast(var array:[String]) { array.removeLast() println(array) // [one, two] } var someArray = ["one", "two", "three"] removeLast(someArray) println(someArray) // [one, two, three] Apparently immutability vs. mutability is a huge topic, but a lot of smart folks think immutability -- structs in this case -- is preferable. Mutable vs immutable objects

结构vs类

[堆栈vs堆] [值vs参考类型]

结构更可取。但是缺省情况下,Struct并不能解决所有问题。通常你会听说值类型是在堆栈上分配的,但这并不总是正确的。只有局部变量被分配到堆栈上

//simple blocks
struct ValueType {}
class ReferenceType {}

struct StructWithRef {
    let ref1 = ReferenceType()
}

class ClassWithRef {
    let ref1 = ReferenceType()
}

func foo() {
    
    //simple  blocks
    let valueType1 = ValueType()
    let refType1 = ReferenceType()
    
    //RetainCount
    //StructWithRef
    let structWithRef1 = StructWithRef()
    let structWithRef1Copy = structWithRef1
    
    print("original:", CFGetRetainCount(structWithRef1 as CFTypeRef)) //1
    print("ref1:", CFGetRetainCount(structWithRef1.ref1)) //2 (originally 3)
    
    //ClassWithRef
    let classWithRef1 = ClassWithRef()
    let classWithRef1Copy = classWithRef1
    
    print("original:", CFGetRetainCount(classWithRef1)) //2 (originally 3)
    print("ref1:", CFGetRetainCount(classWithRef1.ref1)) //1 (originally 2)
     
}

*你不应该使用/依赖retainCount,因为它没有提供有用的信息

检查堆栈或堆

在编译过程中SIL(Swift中间语言)可以优化你的代码

swiftc -emit-silgen -<optimization> <file_name>.swift
//e.g.
swiftc -emit-silgen -Onone file.swift

//emit-silgen -> emit-sil(is used in any case)
//-emit-silgen           Emit raw SIL file(s)
//-emit-sil              Emit canonical SIL file(s)
//optimization: O, Osize, Onone. It is the same as Swift Compiler - Code Generation -> Optimization Level

在那里你可以找到alloc_stack(在堆栈上的分配)和alloc_box(在堆上的分配)

(优化级别(SWIFT_OPTIMIZATION_LEVEL)]

从值类型和引用类型的角度来回答这个问题,从苹果博客的这篇文章来看,它看起来非常简单:

使用值类型[例如struct, enum]: 用==比较实例数据是有意义的 你希望副本有独立的状态 这些数据将在代码中跨多个线程使用 在以下情况下使用引用类型[例如class]: 比较实例标识和===是有意义的 您希望创建共享的、可变的状态

正如在那篇文章中提到的,没有可写属性的类将与结构体的行为相同,但有一点需要注意:结构体最适合线程安全模型——这是现代应用程序架构中日益迫切的需求。

在这些回答中没有注意到的一点是,持有类和结构的变量可以是let,同时仍然允许对对象的属性进行更改,而对于结构则不能这样做。

如果你不希望变量指向另一个对象,但仍然需要修改对象,即在有许多实例变量的情况下,你希望一个接一个地更新,这是很有用的。如果它是一个结构,你必须允许变量被重置为另一个对象使用var,因为在Swift常量值类型正确地允许零突变,而引用类型(类)不这样做。