简体   繁体   English

为什么选择结构而不是类?

[英]Why Choose Struct Over Class?

Playing around with Swift, coming from a Java background, why would you want to choose a Struct instead of a Class?使用 Swift,来自 Java 背景,为什么要选择 Struct 而不是 Class? Seems like they are the same thing, with a Struct offering less functionality.似乎它们是同一回事,结构提供较少的功能。 Why choose it then?那为什么选择它?

According to the very popular WWDC 2015 talk Protocol Oriented Programming in Swift ( video , transcript ), Swift provides a number of features that make structs better than classes in many circumstances.根据非常流行的 WWDC 2015 演讲 Swift 中的面向协议编程(视频成绩单),Swift 提供了许多功能,这些功能使结构在许多情况下比类更好。

Structs are preferable if they are relatively small and copiable because copying is way safer than having multiple references to the same instance as happens with classes.如果结构相对较小且可复制,则结构更可取,因为复制比对同一实例的多个引用更安全,就像在类中发生的那样。 This is especially important when passing around a variable to many classes and/or in a multithreaded environment.当将变量传递给许多类和/或在多线程环境中时,这一点尤其重要。 If you can always send a copy of your variable to other places, you never have to worry about that other place changing the value of your variable underneath you.如果您始终可以将变量的副本发送到其他地方,那么您就不必担心其他地方会更改您下方变量的值。

With Structs, there is much less need to worry about memory leaks or multiple threads racing to access/modify a single instance of a variable.使用 Structs,无需担心内存泄漏或多个线程争相访问/修改变量的单个实例。 (For the more technically minded, the exception to that is when capturing a struct inside a closure because then it is actually capturing a reference to the instance unless you explicitly mark it to be copied). (对于技术性更强的人来说,例外情况是在闭包内捕获结构时,因为除非您明确将其标记为要复制,否则它实际上是在捕获对实例的引用)。

Classes can also become bloated because a class can only inherit from a single superclass.类也可能变得臃肿,因为一个类只能从一个超类继承。 That encourages us to create huge superclasses that encompass many different abilities that are only loosely related.这鼓励我们创建巨大的超类,其中包含许多只是松散相关的不同能力。 Using protocols, especially with protocol extensions where you can provide implementations to protocols, allows you to eliminate the need for classes to achieve this sort of behavior.使用协议,尤其是使用可以提供协议实现的协议扩展,可以消除对实现此类行为的类的需要。

The talk lays out these scenarios where classes are preferred:演讲列出了首选课程的这些场景:

  • Copying or comparing instances doesn't make sense (eg, Window)复制或比较实例没有意义(例如,Window)
  • Instance lifetime is tied to external effects (eg, TemporaryFile)实例生命周期与外部效果相关(例如,TemporaryFile)
  • Instances are just "sinks"--write-only conduits to external state (egCGContext)实例只是“接收器”——外部状态的只写管道(例如CGContext)

It implies that structs should be the default and classes should be a fallback.这意味着结构应该是默认的,而类应该是后备。

On the other hand, The Swift Programming Language documentation is somewhat contradictory:另一方面, The Swift Programming Language文档有些矛盾:

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.一种引用系列中范围的方法,可能封装了一个起始属性和一个长度属性,两者都是 Int 类型。
  • A point in a 3D coordinate system, perhaps encapsulating x, y and z properties, each of type Double. 3D 坐标系中的一个点,可能封装 x、y 和 z 属性,每个属性都是 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.实际上,这意味着大多数自定义数据构造应该是类,而不是结构。

Here it is claiming that we should default to using classes and use structures only in specific circumstances.这里声称我们应该默认使用类并仅在特定情况下使用结构。 Ultimately, you need to understand the real world implication of value types vs. reference types and then you can make an informed decision about when to use structs or classes.最终,您需要了解值类型与引用类型在现实世界中的含义,然后才能就何时使用结构体或类做出明智的决定。 Also, keep in mind that these concepts are always evolving and The Swift Programming Language documentation was written before the Protocol Oriented Programming talk was given.此外,请记住,这些概念总是在不断发展,并且 Swift 编程语言文档是在面向协议的编程演讲之前编写的。

Since struct instances are allocated on stack, and class instances are allocated on heap, structs can sometimes be drastically faster.由于结构体实例是在栈上分配的,而类实例是在堆上分配的,因此结构体有时会快得多。

However, you should always measure it yourself and decide based on your unique use case.但是,您应该始终自己衡量并根据您的独特用例做出决定。

Consider the following example, which demonstrates 2 strategies of wrapping Int data type using struct and class .考虑以下示例,该示例演示了使用structclass包装Int数据类型的 2 种策略。 I am using 10 repeated values are to better reflect real world, where you have multiple fields.我使用 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)
}

Performance is measured using性能测量使用

// 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)")
}

Code can be found at https://github.com/knguyen2708/StructVsClassPerformance代码可以在https://github.com/knguyen2708/StructVsClassPerformance找到

UPDATE (27 Mar 2018) :更新(2018 年 3 月 27 日)

As of Swift 4.0, Xcode 9.2, running Release build on iPhone 6S, iOS 11.2.6, Swift Compiler setting is -O -whole-module-optimization :从 Swift 4.0、Xcode 9.2 开始,在 iPhone 6S、iOS 11.2.6 上运行 Release build,Swift 编译器设置为-O -whole-module-optimization

  • class version took 2.06 seconds class版本用了 2.06 秒
  • struct version took 4.17e-08 seconds (50,000,000 times faster) struct版本耗时 4.17e-08 秒(快 50,000,000 倍)

(I no longer average multiple runs, as variances are very small, under 5%) (我不再平均多次运行,因为差异非常小,低于 5%)

Note : the difference is a lot less dramatic without whole module optimization.注意:如果没有整个模块优化,差异就不那么明显了。 I'd be glad if someone can point out what the flag actually does.如果有人能指出国旗的实际作用,我会很高兴。


UPDATE (7 May 2016) :更新(2016 年 5 月 7 日)

As of Swift 2.2.1, Xcode 7.3, running Release build on iPhone 6S, iOS 9.3.1, averaged over 5 runs, Swift Compiler setting is -O -whole-module-optimization :从 Swift 2.2.1、Xcode 7.3 开始,在 iPhone 6S、iOS 9.3.1 上运行 Release build,平均超过 5 次运行,Swift 编译器设置为-O -whole-module-optimization

  • class version took 2.159942142s class版本花了 2.159942142s
  • struct version took 5.83E-08s (37,000,000 times faster) struct版本耗时 5.83E-08s(快 37,000,000 倍)

Note : as someone mentioned that in real-world scenarios, there will be likely more than 1 field in a struct, I have added tests for structs/classes with 10 fields instead of 1. Surprisingly, results don't vary much.注意:正如有人提到的,在现实世界的场景中,一个结构体中可能会有 1 个以上的字段,我已经为具有 10 个字段而不是 1 个字段的结构体/类添加了测试。令人惊讶的是,结果变化不大。


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

(Ran on struct/class with 1 field, not 10) (在 struct/class 上运行 1 个字段,而不是 10 个)

As of Swift 1.2, Xcode 6.3.2, running Release build on iPhone 5S, iOS 8.3, averaged over 5 runs从 Swift 1.2、Xcode 6.3.2 开始,在 iPhone 5S、iOS 8.3 上运行 Release 版本,平均运行超过 5 次

  • class version took 9.788332333s class版本花了 9.788332333s
  • struct version took 0.010532942s (900 times faster) struct版本需要 0.010532942s(快 900 倍)

OLD RESULTS (from unknown time)旧结果(来自未知时间)

(Ran on struct/class with 1 field, not 10) (在 struct/class 上运行 1 个字段,而不是 10 个)

With release build on my MacBook Pro:在我的 MacBook Pro 上发布版本:

  • The class version took 1.10082 sec class版本耗时 1.10082 秒
  • The struct version took 0.02324 sec (50 times faster) struct版本耗时 0.02324 秒(快 50 倍)

Similarities between structs and classes.结构和类之间的相似之处。

I created gist for this with simple examples.我用简单的例子为此创建了要点。 https://github.com/objc-swift/swift-classes-vs-structures https://github.com/objc-swift/swift-classes-vs-structures

And differences和差异

1. Inheritance. 1.继承。

structures can't inherit in swift.结构不能快速继承。 If you want如果你想

class Vehicle{
}

class Car : Vehicle{
}

Go for an class.去上课。

2. Pass By 2. 路过

Swift structures pass by value and class instances pass by reference. Swift 结构按值传递,类实例按引用传递。

Contextual Differences语境差异

Struct constant and variables结构常量和变量

Example (Used at WWDC 2014)示例(在 WWDC 2014 上使用)

struct Point{
 
   var x = 0.0;
   var y = 0.0;

} 

Defines a struct called Point.定义一个名为 Point 的结构。

var point = Point(x:0.0,y:2.0)

Now if I try to change the x.现在,如果我尝试更改 x. Its a valid expression.它是一个有效的表达式。

point.x = 5

But if I defined a point as constant.但是如果我将一个点定义为常量。

let point = Point(x:0.0,y:2.0)
point.x = 5 //This will give compile time error.

In this case entire point is immutable constant.在这种情况下,整个点都是不可变的常量。

If I used a class Point instead this is a valid expression.如果我使用类 Point 而不是这是一个有效的表达式。 Because in a class immutable constant is the reference to the class itself not its instance variables (Unless those variables defined as constants)因为在一个类中,不可变常量是对类本身的引用,而不是它的实例变量(除非这些变量定义为常量)

Assuming that we know Struct is a value type and Class is a reference type .假设我们知道Struct是一个值类型Class是一个引用类型

If you don't know what a value type and a reference type are then see What's the difference between passing by reference vs. passing by value?如果您不知道值类型和引用类型是什么,那么请参阅按引用传递与按值传递之间的区别什么?

Based on mikeash's post :基于mikeash 的帖子

... Let's look at some extreme, obvious examples first. ...让我们先看一些极端的、明显的例子。 Integers are obviously copyable.整数显然是可复制的。 They should be value types.它们应该是值类型。 Network sockets can't be sensibly copied.网络套接字不能被合理地复制。 They should be reference types.它们应该是引用类型。 Points, as in x, y pairs, are copyable.点,如 x, y 对,是可复制的。 They should be value types.它们应该是值类型。 A controller that represents a disk can't be sensibly copied.无法合理复制代表磁盘的控制器。 That should be a reference type.那应该是一个引用类型。

Some types can be copied but it may not be something you want to happen all the time.某些类型可以复制,但可能不是您希望一直发生的事情。 This suggests that they should be reference types.这表明它们应该是引用类型。 For example, a button on the screen can conceptually be copied.例如,在概念上可以复制屏幕上的按钮。 The copy will not be quite identical to the original.副本不会与原件完全相同。 A click on the copy will not activate the original.单击副本不会激活原件。 The copy will not occupy the same location on the screen.副本不会在屏幕上占据相同的位置。 If you pass the button around or put it into a new variable you'll probably want to refer to the original button, and you'd only want to make a copy when it's explicitly requested.如果您传递按钮或将其放入一个新变量中,您可能想要引用原始按钮,并且您只想在明确请求时制作一个副本。 That means that your button type should be a reference type.这意味着您的按钮类型应该是引用类型。

View and window controllers are a similar example.视图和窗口控制器是一个类似的例子。 They might be copyable, conceivably, but it's almost never what you'd want to do.可以想象,它们可能是可复制的,但这几乎不是您想要做的。 They should be reference types.它们应该是引用类型。

What about model types?模型类型呢? You might have a User type representing a user on your system, or a Crime type representing an action taken by a User.您可能有代表系统上用户的 User 类型,或代表用户采取的操作的 Crime 类型。 These are pretty copyable, so they should probably be value types.这些是非常可复制的,所以它们应该是值类型。 However, you probably want updates to a User's Crime made in one place in your program to be visible to other parts of the program.但是,您可能希望在程序的某个位置对用户犯罪进行的更新对程序的其他部分可见。 This suggests that your Users should be managed by some sort of user controller which would be a reference type .这表明您的用户应该由某种用户控制器管理,这将是一个引用类型 eg例如

struct User {} class UserController { var users: [User] func add(user: User) { ... } func remove(userNamed: String) { ... } func ... }

Collections are an interesting case.集合是一个有趣的案例。 These include things like arrays and dictionaries, as well as strings.这些包括诸如数组和字典之类的东西,以及字符串。 Are they copyable?它们可复制吗? Obviously.明显地。 Is copying something you want to happen easily and often?复制你想要的东西很容易而且经常发生吗? That's less clear.那不太清楚。

Most languages say "no" to this and make their collections reference types.大多数语言对此说“不”,并使它们的集合引用类型。 This is true in Objective-C and Java and Python and JavaScript and almost every other language I can think of.在 Objective-C 和 Java、Python 和 JavaScript 以及我能想到的几乎所有其他语言中都是如此。 (One major exception is C++ with STL collection types, but C++ is the raving lunatic of the language world which does everything strangely.) (一个主要的例外是带有 STL 集合类型的 C++,但 C++ 是语言世界的狂热疯子,它做任何事情都很奇怪。)

Swift said "yes," which means that types like Array and Dictionary and String are structs rather than classes. Swift 说“是”,这意味着像 Array、Dictionary 和 String 这样的类型是结构而不是类。 They get copied on assignment, and on passing them as parameters.它们在赋值时被复制,并将它们作为参数传递。 This is an entirely sensible choice as long as the copy is cheap, which Swift tries very hard to accomplish.只要副本便宜,这是一个完全明智的选择,Swift 非常努力地做到这一点。 ... ...

I personally don't name my classes like that.我个人不会这样命名我的课程。 I usually name mine UserManager instead of UserController but the idea is the same我通常命名我的UserManager而不是UserController但想法是一样的

In addition don't use class when you have to override each and every instance of a function ie them not having any shared functionality.此外,当您必须覆盖函数的每个实例时,不要使用类,即它们没有任何共享功能。

So instead of having several subclasses of a class.因此,而不是有一个类的几个子类。 Use several structs that conform to a protocol.使用多个符合协议的结构。


Another reasonable case for structs is when you want to do a delta/diff of your old and new model.结构的另一个合理情况是当您想要对旧模型和新模型进行增量/差异时。 With references types you can't do that out of the box.对于引用类型,您不能开箱即用。 With value types the mutations are not shared.对于值类型,不共享突变。

Here are some other reasons to consider:以下是一些需要考虑的其他原因:

  1. structs get an automatic initializer that you don't have to maintain in code at all. structs 获得一个自动初始化程序,您根本不必在代码中维护它。

     struct MorphProperty { var type : MorphPropertyValueType var key : String var value : AnyObject enum MorphPropertyValueType { case String, Int, Double } } var m = MorphProperty(type: .Int, key: "what", value: "blah")

To get this in a class, you would have to add the initializer, and maintain the intializer...要在类中获得它,您必须添加初始化程序,并维护初始化程序...

  1. Basic collection types like Array are structs.Array这样的基本集合类型是结构。 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]
  2. 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 可变对象与不可变对象

Some advantages:一些优点:

  • automatically threadsafe due to not being shareable由于不可共享而自动线程安全
  • uses less memory due to no isa and refcount (and in fact is stack allocated generally)由于没有 isa 和 refcount,使用更少的内存(实际上通常是堆栈分配的)
  • methods are always statically dispatched, so can be inlined (though @final can do this for classes)方法总是静态调度的,因此可以内联(尽管 @final 可以为类执行此操作)
  • easier to reason about (no need to "defensively copy" as is typical with NSArray, NSString, etc...) for the same reason as thread safety出于与线程安全相同的原因,更容易推理(无需像 NSArray、NSString 等那样典型的“防御性复制”)

Structure is much more faster than Class.结构比类快得多。 Also, if you need inheritance then you must use Class.此外,如果您需要继承,那么您必须使用 Class。 Most important point is that Class is reference type whereas Structure is value type.最重要的一点是 Class 是引用类型,而 Structure 是值类型。 for example,例如,

class Flight {
    var id:Int?
    var description:String?
    var destination:String?
    var airlines:String?
    init(){
        id = 100
        description = "first ever flight of Virgin Airlines"
        destination = "london"
        airlines = "Virgin Airlines"
    } 
}

struct Flight2 {
    var id:Int
    var description:String
    var destination:String
    var airlines:String  
}

now lets create instance of both.现在让我们创建两者的实例。

var flightA = Flight()

var flightB = Flight2.init(id: 100, description:"first ever flight of Virgin Airlines", destination:"london" , airlines:"Virgin Airlines" )

now lets pass these instance to two functions which modify the id, description, destination etc..现在让我们将这些实例传递给两个修改 id、描述、目的地等的函数。

func modifyFlight(flight:Flight) -> Void {
    flight.id = 200
    flight.description = "second flight of Virgin Airlines"
    flight.destination = "new york"
    flight.airlines = "Virgin Airlines"
}

also,还,

func modifyFlight2(flight2: Flight2) -> Void {
    var passedFlight = flight2
    passedFlight.id = 200
    passedFlight.description = "second flight from virgin airlines" 
}

so,所以,

modifyFlight(flight: flightA)
modifyFlight2(flight2: flightB)

now if we print the flightA's id and description, we get现在如果我们打印 FlightA 的 id 和描述,我们会得到

id = 200
description = "second flight of Virgin Airlines"

Here, we can see the id and description of FlightA is changed because the parameter passed to the modify method actually points to the memory address of flightA object(reference type).在这里,我们可以看到 FlightA 的 id 和 description 发生了变化,因为传递给 modify 方法的参数实际上指向了 flightA 对象(引用类型)的内存地址。

now if we print the id and description of FLightB instance we get,现在如果我们打印我们得到的 FLightB 实例的 id 和描述,

id = 100
description = "first ever flight of Virgin Airlines"

Here we can see that the FlightB instance is not changed because in modifyFlight2 method, actual instance of Flight2 is passes rather than reference ( value type).在这里我们可以看到 FlightB 实例没有改变,因为在 modifyFlight2 方法中,Flight2 的实际实例是传递而不是引用(值类型)。

Structs are value type and Classes are reference type Structsvalue typeClassesreference type

  • Value types are faster than Reference types值类型比引用类型更快
  • Value type instances are safe in a multi-threaded environment as multiple threads can mutate the instance without having to worry about the race conditions or deadlocks值类型实例在多线程环境中是安全的,因为多个线程可以改变实例而不必担心竞争条件或死锁
  • Value type has no references unlike reference type;与引用类型不同,值类型没有引用; therefore there is no memory leaks.因此没有内存泄漏。

Use a value type when:在以下情况下使用value类型:

  • You want copies to have independent state, the data will be used in code across multiple threads您希望副本具有独立状态,数据将在跨多个线程的代码中使用

Use a reference type when:在以下情况下使用reference类型:

  • You want to create shared, mutable state.您想要创建共享的、可变的状态。

Further information could be also found in the Apple documentation还可以在 Apple 文档中找到更多信息

https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html


Additional Information附加信息

Swift value types are kept in the stack. Swift 值类型保存在堆栈中。 In a process, each thread has its own stack space, so no other thread will be able to access your value type directly.在一个进程中,每个线程都有自己的堆栈空间,因此没有其他线程能够直接访问您的值类型。 Hence no race conditions, locks, deadlocks or any related thread synchronization complexity.因此没有竞争条件、锁、死锁或任何相关的线程同步复杂性。

Value types do not need dynamic memory allocation or reference counting, both of which are expensive operations.值类型不需要动态内存分配或引用计数,这两者都是昂贵的操作。 At the same time methods on value types are dispatched statically.同时,值类型的方法是静态分派的。 These create a huge advantage in favor of value types in terms of performance.这些在性能方面创造了有利于值类型的巨大优势。

As a reminder here is a list of Swift提醒一下,这里有一个 Swift 列表

Value types:值类型:

  • Struct结构
  • Enum枚举
  • Tuple元组
  • Primitives (Int, Double, Bool etc.)原语(Int、Double、Bool 等)
  • Collections (Array, String, Dictionary, Set)集合(数组、字符串、字典、集合)

Reference types:参考类型:

  • Class班级
  • Anything coming from NSObject任何来自 NSObject 的东西
  • Function功能
  • Closure关闭

Answering the question from the perspective of value types vs reference types, from this Apple blog post it would appear very simple:从值类型与引用类型的角度回答这个问题,从这篇 Apple 博客文章来看,它看起来非常简单:

Use a value type [eg struct, enum] when:在以下情况下使用值类型 [例如 struct, enum]:

  • Comparing instance data with == makes sense用 == 比较实例数据是有道理的
  • You want copies to have independent state您希望副本具有独立状态
  • The data will be used in code across multiple threads数据将在跨多个线程的代码中使用

Use a reference type [eg class] when:在以下情况下使用引用类型 [例如类]:

  • Comparing instance identity with === makes sense将实例标识与 === 进行比较是有道理的
  • You want to create shared, mutable state你想创建共享的、可变的状态

As mentioned in that article, a class with no writeable properties will behave identically with a struct, with (I will add) one caveat: structs are best for thread-safe models -- an increasingly imminent requirement in modern app architecture.正如那篇文章中提到的,没有可写属性的类的行为与结构相同,但(我将添加)一个警告:结构最适合线程安全模型——这是现代应用程序架构中日益迫切的要求。

With classes you get inheritance and are passed by reference, structs do not have inheritance and are passed by value.使用类获得继承并通过引用传递,结构没有继承并通过值传递。

There are great WWDC sessions on Swift, this specific question is answered in close detail in one of them.有很多关于 Swift 的 WWDC 会议,其中一个详细回答了这个特定问题。 Make sure you watch those, as it will get you up to speed much more quickly then the Language guide or the iBook.请务必观看这些内容,因为与语言指南或 iBook 相比,它可以让您更快上手。

I wouldn't say that structs offer less functionality.我不会说结构提供的功能较少。

Sure, self is immutable except in a mutating function, but that's about it.当然, self 是不可变的,除了在变异函数中,但仅此而已。

Inheritance works fine as long as you stick to the good old idea that every class should be either abstract or final.只要您坚持每个类都应该是抽象类或最终类的旧想法,继承就可以正常工作。

Implement abstract classes as protocols and final classes as structs.将抽象类实现为协议,将最终类实现为结构。

The nice thing about structs is that you can make your fields mutable without creating shared mutable state because copy on write takes care of that :)结构体的好处是你可以让你的字段可变而不用创建共享可变状态,因为写时复制可以解决这个问题:)

That's why the properties / fields in the following example are all mutable, which I would not do in Java or C# or swift classes .这就是为什么以下示例中的属性/字段都是可变的,我不会在 Java 或 C# 或 swift classes 中这样做

Example inheritance structure with a bit of dirty and straightforward usage at the bottom in the function named "example":示例继承结构,在名为“example”的函数底部有一些肮脏和直接的用法:

protocol EventVisitor
{
    func visit(event: TimeEvent)
    func visit(event: StatusEvent)
}

protocol Event
{
    var ts: Int64 { get set }

    func accept(visitor: EventVisitor)
}

struct TimeEvent : Event
{
    var ts: Int64
    var time: Int64

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }
}

protocol StatusEventVisitor
{
    func visit(event: StatusLostStatusEvent)
    func visit(event: StatusChangedStatusEvent)
}

protocol StatusEvent : Event
{
    var deviceId: Int64 { get set }

    func accept(visitor: StatusEventVisitor)
}

struct StatusLostStatusEvent : StatusEvent
{
    var ts: Int64
    var deviceId: Int64
    var reason: String

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }

    func accept(visitor: StatusEventVisitor)
    {
        visitor.visit(self)
    }
}

struct StatusChangedStatusEvent : StatusEvent
{
    var ts: Int64
    var deviceId: Int64
    var newStatus: UInt32
    var oldStatus: UInt32

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }

    func accept(visitor: StatusEventVisitor)
    {
        visitor.visit(self)
    }
}

func readEvent(fd: Int) -> Event
{
    return TimeEvent(ts: 123, time: 56789)
}

func example()
{
    class Visitor : EventVisitor
    {
        var status: UInt32 = 3;

        func visit(event: TimeEvent)
        {
            print("A time event: \(event)")
        }

        func visit(event: StatusEvent)
        {
            print("A status event: \(event)")

            if let change = event as? StatusChangedStatusEvent
            {
                status = change.newStatus
            }
        }
    }

    let visitor = Visitor()

    readEvent(1).accept(visitor)

    print("status: \(visitor.status)")
}

In Swift, a new programming pattern has been introduced known as Protocol Oriented Programming.在 Swift 中,引入了一种新的编程模式,称为面向协议的编程。

Creational Pattern:创作模式:

In swift, Struct is a value types which are automatically cloned.在 swift 中, Struct 是一种自动克隆的值类型 Therefore we get the required behavior to implement the prototype pattern for free.因此,我们获得了免费实现原型模式所需的行为。

Whereas classes are the reference type, which is not automatically cloned during the assignment.是引用类型,在赋值过程中不会自动克隆。 To implement the prototype pattern, classes must adopt the NSCopying protocol.为了实现原型模式,类必须采用NSCopying协议。


Shallow copy duplicates only the reference, that points to those objects whereas deep copy duplicates object's reference.浅拷贝复制指向这些对象的引用,而深拷贝则复制对象的引用。


Implementing deep copy for each reference type has become a tedious task.为每个引用类型实现深拷贝已经成为一项繁琐的任务。 If classes include further reference type, we have to implement prototype pattern for each of the references properties.如果类包含更多的引用类型,我们必须为每个引用属性实现原型模式。 And then we have to actually copy the entire object graph by implementing the NSCopying protocol.然后我们必须通过实现NSCopying协议来实际复制整个对象图。

class Contact{
  var firstName:String
  var lastName:String
  var workAddress:Address // Reference type
}

class Address{
   var street:String
   ...
} 

By using structs and enums , we made our code simpler since we don't have to implement the copy logic.通过使用structs 和 enums ,我们使代码更简单,因为我们不必实现复制逻辑。

Struct vs Class结构与类

[Stack vs Heap] [堆栈与堆]
[Value vs Reference type] [值与引用类型]

Struct is more preferable . Struct可取 But Struct does not solve all issues by default.但默认情况下, Struct并不能解决所有问题。 Usually you can hear that value type is allocated on stack, but it is not always true.通常您可以听到value type是在堆栈上分配的,但并非总是如此。 Only local variables are allocated on stack栈上只分配局部变量

//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)
     
}

*You should not use/rely on retainCount , because it does not say useful information *你应该使用/依赖retainCount ,因为它没有说的有用信息

During compiling SIL(Swift Intermediate Language) can optimize you code在编译 SIL(Swift Intermediate Language) 期间可以优化你的代码

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

There you can find alloc_stack (allocation on stack) andalloc_box (allocation on heap)在那里你可以找到alloc_stack (堆栈上的分配)和alloc_box (堆上的分配)

Many Cocoa APIs require NSObject subclasses, which forces you into using class.许多 Cocoa API 需要 NSObject 子类,这迫使您使用类。 But other than that, you can use the following cases from Apple's Swift blog to decide whether to use a struct / enum value type or a class reference type.但除此之外,您可以使用 Apple 的 Swift 博客中的以下案例来决定是使用 struct / enum 值类型还是类引用类型。

https://developer.apple.com/swift/blog/?id=10 https://developer.apple.com/swift/blog/?id=10

One point not getting attention in these answers is that a variable holding a class vs a struct can be a let while still allowing changes on the object's properties, while you cannot do this with a struct.在这些答案中没有引起注意的一点是,保存类与结构的变量可以是let同时仍然允许更改对象的属性,而您不能使用结构来做到这一点。

This is useful if you don't want the variable to ever point to another object, but still need to modify the object, ie in the case of having many instance variables that you wish to update one after another.如果您不希望变量指向另一个对象,但仍需要修改该对象,即在您希望一个接一个地更新许多实例变量的情况下,这将非常有用。 If it is a struct, you must allow the variable to be reset to another object altogether using var in order to do this, since a constant value type in Swift properly allows zero mutation, while reference types (classes) don't behave this way.如果它是一个结构体,则必须允许使用var将变量完全重置为另一个对象才能执行此操作,因为 Swift 中的常量值类型正确地允许零突变,而引用类型(类)不会以这种方式运行.

As struct are value types and you can create the memory very easily which stores into stack.Struct can be easily accessible and after the scope of the work it's easily deallocated from the stack memory through pop from the top of the stack.由于结构是值类型,您可以非常轻松地创建存储到堆栈中的内存。结构可以轻松访问,并且在工作范围之后,它可以通过从堆栈顶部弹出来轻松地从堆栈内存中释放。 On the other hand class is a reference type which stores in heap and changes made in one class object will impact to other object as they are tightly coupled and reference type.All members of a structure are public whereas all the members of a class are private.另一方面,类是一种存储在堆中的引用类型,在一个类对象中所做的更改将影响其他对象,因为它们紧密耦合且引用类型。结构的所有成员都是公共的,而类的所有成员都是私有的.

The disadvantages of struct is that it can't be inherited . struct 的缺点是不能被继承。

  • Structure and class are user defied data types结构和类是用户定义的数据类型

  • By default, structure is a public whereas class is private默认情况下,结构是公共的,而类是私有的

  • Class implements the principal of encapsulation类实现封装原理

  • Objects of a class are created on the heap memory在堆内存上创建类的对象

  • Class is used for re usability whereas structure is used for grouping the data in the same structure类用于可重用性,而结构用于将数据分组在同一结构中

  • Structure data members cannot be initialized directly but they can be assigned by the outside the structure结构数据成员不能直接初始化,但可以由结构外部分配

  • Class data members can be initialized directly by the parameter less constructor and assigned by the parameterized constructor类数据成员可以由无参数构造函数直接初始化,由参数化构造函数赋值

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么 Swift 类需要 init 而不需要 Swift struct - Why Swift class need init but not Swift struct 为什么forEach中的迭代器对于结构是不可变的,但对于类却是可变的? - Why are iterators in forEach immutable for a struct but mutable for a class? 使用struct over class是否会对允许的数据大小产生影响? - Will using struct over class, will have impact on the allowable data size? 在Swift中必须在类,结构和枚举之间进行选择时,我应该怎么推理? - How should I reason when I have to choose between a class, struct and enum in Swift? 苹果为何选择使用嵌套的结构类型来替换“字符串”类型参数? - Why does Apple choose to use nested struct types to replace 'String' type parameter? 关于内存消耗:为什么选择静态let over(计算)静态var? - In regard of memory consumption: Why choose static let over (computed) static var? 为什么我们应该在单例模式中使用struct和class函数? - Why we should use struct and class function in singleton pattern? 为什么 Diffable Datasources 以不同的方式对待 class 和结构类型? - Why do Diffable Datasources treat class and struct types differently? 为什么class变量在struct中没有值迅速 - why does the class variable does not have value in struct in swift 为什么Swift中的公共类/结构需要一个显式的公共初始化器? - Why does a public class/struct in Swift require an explicit public initializer?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM