gpt4 book ai didi

Why Choose Struct Over Class?(为什么选择Struct而不是Class?)

转载 作者:bug小助手 更新时间:2023-10-28 09:37:35 26 4
gpt4 key购买 nike



Playing around with Swift, coming from a Java background, why would you want to choose a Struct instead of a Class? Seems like they are the same thing, with a Struct offering less functionality. Why choose it then?

玩Swift,来自Java背景,为什么要选择Struct而不是Class?它们似乎是一回事,只是Struct提供的功能较少。那么为什么要选择它呢?


更多回答

Structures are always copied when they are passed around in your code, and do not use reference counting. source: developer.apple.com/library/prerelease/ios/documentation/swift/…

结构在代码中传递时始终被复制,并且不使用引用计数。消息来源:developer.apple.com/library/prerelease/ios/documentation/swift/…

I would say that structs are more appropriated to hold data, not logic. To speak in Java terms, imagine structs as "Value Objects".

我想说的是,结构更适合保存数据,而不是逻辑。用Java术语来说,可以将结构想象为“值对象”。

I'm amazed in this whole conversation there is no direct mention of copy-on-write a.k.a. lazy copy. Any concerns about struct copy performance are mostly moot on account of this design.

在整个对话中,我感到惊讶的是,没有直接提到写时复制(Copy-on-Well,又名)。懒惰的抄袭。考虑到这种设计,任何关于结构复制性能的担忧都是没有意义的。

Choosing a struct over a class is not a matter of opinion. There are specific reasons to choose one or the other.

选择结构而不是类并不是一个观点问题。选择其中之一是有特定原因的。

I highly recommend to see Why Array is not threadSafe. It's related because Arrays & Structs are both value types. All answers here mention that with structs/arrays/value types will never have a thread Safety issue, but there is a corner case in which you will.

我强烈建议查看为什么阵列不是线程安全。这是相关的,因为数组和结构都是值类型。这里的所有答案都提到,使用结构/数组/值类型永远不会有线程安全问题,但有一个特殊的情况会出现。

优秀答案推荐

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.

根据SWIFT中非常流行的WWDC 2015面向谈话协议的编程(视频、文字记录),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. (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).

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


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 (e.g., Window)

  • Instance lifetime is tied to external effects (e.g., TemporaryFile)

  • Instances are just "sinks"--write-only conduits to external state (e.g., 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:

另一方面,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.



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编程语言文档是在面向协议编程演讲之前编写的。



This answer was originally about difference in performance between struct and class. Unfortunately there are too much controversy around the method I used for measuring. I left it below, but please don't read too much into it. I think after all these years, it has become clear in Swift community that struct (along with enum) is always preferred due to its simplicity and safety.

这个答案最初是关于结构和类之间的性能差异。不幸的是,关于我用来测量的方法有太多的争议。我把它留在下面了,但请不要读太多。我认为经过这么多年,Swift社区已经清楚地看到,由于其简单性和安全性,Struct(以及enum)一直是首选。


If performance is important to your app, do measure it yourself. I still think most of the time struct performance is superior, but the best answer is just as someone said in the comments: it depends.

如果性能对你的应用程序很重要,那么你一定要自己衡量它。我仍然认为大多数时候结构的性能是优越的,但最好的答案就像有人在评论中说的那样:这要看情况。


=== OLD ANSWER ===

=旧答案=


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. I am using 10 repeated values are to better reflect real world, where you have multiple fields.

考虑下面的示例,它演示了使用结构和类包装Int数据类型的两种策略。我使用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上运行版本构建,SWIFT编译器设置为-O-整体-模块-优化:



  • class version took 2.06 seconds

  • struct version took 4.17e-08 seconds (50,000,000 times faster)


(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-整个模块-优化:



  • class version took 2.159942142s

  • struct version took 5.83E-08s (37,000,000 times faster)


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.

注意:有人提到在现实世界的场景中,一个结构中可能会有多个字段,我已经为结构/类添加了10个而不是1个字段的测试。令人惊讶的是,结果并没有太大的差异。




ORIGINAL RESULTS (1 June 2014):

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


(Ran on struct/class with 1 field, not 10)

(在具有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 build,平均运行超过5次



  • class version took 9.788332333s

  • struct version took 0.010532942s (900 times faster)




OLD RESULTS (from unknown time)

旧结果(来自未知时间)


(Ran on struct/class with 1 field, not 10)

(在具有1个字段而不是10个字段的结构/类上运行)


With release build on my MacBook Pro:

在我的MacBook Pro上发布版本:



  • The class version took 1.10082 sec

  • The struct version took 0.02324 sec (50 times faster)



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.


structures can't inherit in swift. If you want

结构不能快速继承。如果你想的话


class Vehicle{
}

class Car : Vehicle{
}

Go for an class.

去上课吧。


2. Pass By


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. Its a valid expression.

现在,如果我试着改变x,它是一个有效的表达式。


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. Because in a class immutable constant is the reference to the class itself not its instance variables (Unless those variables defined as constants)

如果我使用的是类Point,这是一个有效的表达式。因为在类中,不变的常量是对类本身的引用,而不是它的实例变量(除非这些变量定义为常量)



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. 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. 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
. e.g



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. (One major exception
is C++ with STL collection types, but C++ is the raving lunatic of the
language world which does everything strangely.)



Swift said "yes," which means that types like Array and Dictionary and
String are structs rather than classes. 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.
...




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.



    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. 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)

  • methods are always statically dispatched, so can be inlined (though @final can do this for classes)

  • easier to reason about (no need to "defensively copy" as is typical with NSArray, NSString, etc...) for the same reason as thread safety



Structs are value type and Classes are reference 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:

在以下情况下使用值类型:




  • You want copies to have independent state, the data will be used in
    code across multiple threads



Use a reference type when:

在以下情况下使用引用类型:




  • 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. 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.

SWIFT值类型保留在堆栈中。在进程中,每个线程都有自己的堆栈空间,因此没有其他线程能够直接访问您的值类型。因此,没有争用条件、锁、死锁或任何相关的线程同步复杂性。



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.)

  • Collections (Array, String, Dictionary, Set)



Reference types:

引用类型:




  • Class

  • Anything coming from NSObject

  • Function

  • Closure



Structure is much more faster than Class. Also, if you need inheritance then you must use Class. Most important point is that Class is reference type whereas Structure is value type. 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

现在,如果我们打印航班A的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和描述被更改了,因为传递给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实例没有改变,因为在modfyFlight2方法中,Flight2的实际实例是PASSES而不是Reference(值类型)。



Answering the question from the perspective of value types vs reference types, from this Apple blog post it would appear very simple:

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




Use a value type [e.g. struct, enum] when:




  • 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 [e.g. 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.

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



Struct vs Class

结构与类


[Stack vs Heap]

[Value vs Reference type]

[堆栈与堆] [值与引用类型]


Struct is more preferable. But Struct does not solve all issues by default. Usually you can hear that value type is allocated on stack, but it is not always true. Only local variables are allocated on stack

结构更可取。但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)

}

*You should not use/rely on retainCount, because it does not say useful information

*您不应该使用/依赖retainCount,因为它没有说明有用的信息


To check stack or heap

检查堆栈或堆


During compiling Swift Intermediate Language(SIL) can optimize you code

在编译期间,SWIFT中间语言(SILL)可以优化您的代码


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) and alloc_box(allocation on heap)

在那里可以找到ALLOC_STACK(堆栈上的分配)和ALLOC_BOX(堆上的分配)


[Optimization Level(SWIFT_OPTIMIZATION_LEVEL)]

[优化级别(SWIFT_OPTIMIZATION_LEVEL)]



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. Make sure you watch those, as it will get you up to speed much more quickly then the Language guide or the iBook.

SWIFT上有很多很棒的WWDC会议,其中一个会议详细回答了这个具体问题。一定要看这些,因为它们会让你比语言指南或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类中不能做到的。



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.




Creational Pattern:

创意模式:



In swift, Struct is a value types which are automatically cloned. Therefore we get the required behavior to implement the prototype pattern for free.

在SWIFT中,Struct是一种自动克隆的值类型。因此,我们免费获得实现原型模式所需的行为。



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.

而类是引用类型,在赋值过程中不会自动克隆。要实现Prototype模式,类必须采用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.

通过使用结构和枚举,我们使代码变得更简单,因为我们不必实现复制逻辑。



Many Cocoa APIs require NSObject subclasses, which forces you into using class. 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.

许多Cocoa API需要NSObject子类,这会迫使您使用类。但除此之外,您可以使用Apple Swift博客中的以下案例来决定是使用结构/枚举值类型还是使用类引用类型。



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, i.e. 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.

由于Struct是值类型,您可以非常容易地创建存储到堆栈中的内存。Struct可以很容易地访问,在工作范围之后,它很容易通过从堆栈顶部弹出从堆栈内存中释放。另一方面,类是一种引用类型,它存储在堆中,在一个类对象中所做的更改会影响其他对象,因为它们是紧耦合的引用类型。一个结构的所有成员都是公共的,而一个类的所有成员都是私有的。



The disadvantages of struct is that it can't be inherited .

结构的缺点是它不能被继承。



Structures and classes are good choices for storing data and modeling behavior in your apps, but their similarities can make it difficult to choose one over the other.

结构和类是在应用程序中存储数据和建模行为的很好的选择,但它们的相似性可能会使您很难选择其中之一。


Consider the following recommendations to help choose which option makes sense when adding a new data type to your app.

考虑以下建议,以帮助选择在向应用程序添加新数据类型时哪个选项是有意义的。



  • Use structures by default.

  • Use classes when you need Objective-C interoperability.

  • Use classes when you need to control the identity of the data
    you’re modeling.

  • Use structures along with protocols to adopt behavior by sharing
    implementations.


Ref- Apple documentation

参考- Apple文档


更多回答

@ElgsQianChen the whole point of this writeup is that struct should be chosen by default and class should only be used when necessary. Structs are much safer and bug free, especially in a multithreaded environment. Yes, you can always use a class in place of a struct, but structs are preferable.

@ElgsQianChen这篇文章的重点是,默认情况下应该选择struct,只有在必要时才应该使用类。结构要安全得多,而且没有错误,尤其是在多线程环境中。是的,您总是可以使用类来代替结构,但结构更可取。

@drewag That seems to be the exact opposite of what it is saying. It was saying that a class should be the default that you use, not a structure In practice, this means that most custom data constructs should be classes, not structures. Can you explain to me how, after reading that, you get that most data sets should be structures and not classes? They gave a specific set of rules when something should be a struct and pretty much said "all other scenarios a class is better."

@drewag这似乎与它所说的完全相反。它是说类应该是您使用的默认类,而不是实践中的结构,这意味着大多数定制数据构造应该是类,而不是结构。你能向我解释一下,在读完这篇文章后,你怎么会明白大多数数据集应该是结构而不是类?他们给出了一组特定的规则,当某些东西应该是结构时,几乎是说“所有其他情况下,类都更好”。

Last line should say, "My personal advice is the opposite of the documentation:"... and then it's a great answer!

最后一行应该是:“我的个人建议与文档相反:”……然后这就是一个很好的答案!

Swift 2.2 book still says use classes in most situations.

SWIFT 2.2版的书仍然说在大多数情况下使用类。

Struct over Class is definitely reduce the complexity. But what is the implication on memory usage when structs becomes the default choice. When things get copied everywhere instead of reference it should increase the memory usage by the app. Shouldn't it?

Strt Over Class无疑降低了复杂性。但是,当结构成为默认选择时,对内存使用的影响是什么呢?当内容被复制到各处而不是引用时,应该会增加应用程序的内存使用量。不是吗?

True, but it seems that copying a bunch of structs around would be slower than copying a reference to a single object. In other words it's faster to copy a single pointer around than to copy an arbitrarily large block of memory.

没错,但复制一堆结构似乎比复制对单个对象的引用要慢。换句话说,复制单个指针比复制任意大小的内存块要快。

-1 This test is not a good example because there is only a single var on the struct. Note that if you add several values and an object or two, the struct version gets comparable to the class version. The more vars you add, the slower the struct version gets.

该测试不是一个好的例子,因为结构上只有一个变量。请注意,如果添加几个值和一个或两个对象,结构版本将与类版本相当。添加的变量越多,结构版本就变得越慢。

@joshrl got your point, but an example is "good" or not depends on specific situation. This code was extracted from my own app, so it is a valid use case, and using structs did massively improves performance for my app. It's just probably not a common use case (well, the common use case is, for most apps, no one cares about how fast data can be passed around, since the bottleneck happens somewhere else, e.g. network connections, anyway, optimization is not that critical when you have GHz devices with GBs or RAM).

@joshl明白你的意思,但举个例子,好不好要看具体情况而定。这段代码是从我自己的应用程序中提取的,所以它是一个有效的用例,使用结构确实极大地提高了我的应用程序的性能。这可能不是一个常见的用例(嗯,常见的用例是,对于大多数应用程序来说,没有人关心数据传输的速度,因为瓶颈发生在其他地方,例如网络连接,无论如何,当你有GB或RAM的GHz设备时,优化并不是那么关键)。

As far as I understand copying in swift is optimized to happen at WRITE time. Which means that there is no physical memory copy made unless the new copy is about to be altered.

据我所知,SWIFT中的复制优化为在写入时进行。这意味着除非将要更改新的副本,否则不会制作物理存储器副本。

The 2016 results look flawed. The A9 in an iPhone 6S runs at 1.8 GHz, which means a clock cycle takes ~5.6E-10 seconds. Your benchmark took 5.83E-08s, in other words: about 100 clock cycles. That would mean your CPU did 100,000 add operations per cycle. My guess is that either the compiler removed the entire block because it figured that the result is never used or it figured that you don't use any of the intermediate results and just replaced the end result with a static one calculated at compile time. Either way you probably just measured the measurement overhead itself.

2016年的结果看起来有缺陷。IPhone 6S中的A9运行频率为1.8 GHz,这意味着一个时钟周期需要大约5.6E-10秒。你的基准测试用了5.83E-08秒,换句话说:大约100个时钟周期。这意味着您的CPU每个周期执行100,000次加法操作。我的猜测是,要么编译器删除了整个块,因为它认为结果永远不会被使用,要么它认为您不使用任何中间结果,只是用在编译时计算的静态结果替换了最终结果。无论哪种方式,您可能只是测量了测量开销本身。

You can inherit structs in Swift gist.github.com/AliSoftware/9e4946c8b6038572d678

您可以在SWIFT gist.github.com/AliSoftware/9e4946c8b6038572d678中继承结构

The above gist is about how we can achieve inheritance flavors for struct. You will see syntax like. A: B. It is struct called A implements protocol called B. Apple documentation clearly mention that struct doesn't support pure inheritance and it doesn't.

上面的要点是关于我们如何实现结构的继承风格。您将看到类似这样的语法。答:B。它是称为A实现协议的结构,称为B。Apple文档清楚地提到,结构不支持纯继承,也不支持纯继承。

man that last paragraph of yours was great. I always knew that you could change constants...but then time to time I saw where you couldn't so I was baffled. This distinction made it visible

伙计,你的最后一段太棒了。我一直知道你可以改变常量……但后来我不时地发现你不能改变的地方,所以我很困惑。这种区别使它变得可见

Exactly the kind of explanation I was looking for. Nice write up :)

这正是我想要的解释。写得不错:)

Very helpful controller example

非常有用的控制器示例

@AskP I emailed mike himself and got that extra piece of code :)

@AskP我给Mike本人发了电子邮件,得到了额外的一段代码:)

Years of experience in simple strings. Thanks.

多年的简单琴弦演奏经验。谢谢。

It's true you get automatic initialisers. You also get an empty initialiser when all the properties are Optional. But, if you have a struct in a Framework you need to actually write the initialiser yourself if you want it to be available outside internal scope.

你会得到自动初始化,这是事实。当所有属性都是可选的时,您还会得到一个空的初始值设定项。但是,如果您在框架中有一个结构,如果您希望它在内部作用域之外可用,则需要实际自己编写初始化器。

@Abizern confirmed -- stackoverflow.com/a/26224873/8047 -- and man that is annoying.

@Abizern确认了--Stackoverflow.com/a/26224873/8047.--这是一个令人讨厌的男人。

@Abizern there are great reasons for everything in Swift, but every time something is true in one place and not in another, the developer has to know more stuff. I guess here's where I'm supposed to say, "it's exciting to work in such a challenging language!"

@Abizern SWIFT中的每一件事都有很好的理由,但每次一些事情在一个地方是正确的,在另一个地方不是,开发者必须知道更多的东西。我想这就是我应该说的话:“在这样一种具有挑战性的语言中工作是令人兴奋的!”

Can I also add that it's not the immutability of structs that makes them useful (although it's a very good thing). You can mutate structs, But you have to mark the methods as mutating so you are explicit about what functions change their state. But their nature as value types is what is important. If you declare a struct with let you can't call any mutating functions on it. The WWDC 15 video on Better programming through value types is an excellent resource on this.

我还可以补充说,结构的不变性并不是使它们有用的原因(尽管这是一件非常好的事情)。您可以更改结构,但必须将方法标记为更改,这样才能明确说明哪些函数更改了它们的状态。但它们作为价值类型的本质才是重要的。如果使用let声明结构,则不能在其上调用任何变异函数。WWDC 15关于通过值类型更好地编程的视频就是关于这一点的极好资源。

Thanks @Abizern, I never really understood this before reading your comment. For objects, let vs. var isn't much of a difference, but for structs it's huge. Thanks for pointing this out.

谢谢@Abizern,在阅读你的评论之前,我从来没有真正理解过这一点。对于对象,let和var没有太大区别,但对于结构来说,差别很大。谢谢你指出这一点。

Not sure if it's outside the scope of this answer, but can you explain (or link, I guess) the "methods are always statically dispatched" point?

我不确定这是否超出了这个答案的范围,但您能解释(或链接,我猜)“方法总是静态调度的”这一点吗?

Sure. I can also attach a caveat to it. The purpose of dynamic dispatch is to pick an implementation when you don't know up front which one to use. In Swift, that can either be due to inheritance (might be overridden in a subclass), or due to the function being generic (you don't know what the generic parameter will be). Structs can't be inherited from and whole-module-optimization + generic specialization mostly eliminates the unknown generics, so methods can just be called directly rather than having to look up what to call. Unspecialized generics still do dynamic dispatch for structs though

好的。我还可以给它附加一个警告。动态分派的目的是在您事先不知道要使用哪种实现时挑选一个实现。在SWIFT中,这可能是因为继承(可能在子类中被覆盖),也可能是因为函数是泛型的(您不知道泛型参数是什么)。结构不能从继承,整个模块优化+泛型专门化主要消除了未知的泛型,因此方法可以直接调用,而不必查找要调用的内容。不过,非专门化泛型仍然对结构进行动态调度

Thanks, great explanation. So we're expecting more runtime speed, or less ambiguity from an IDE perspective, or both?

谢谢,很好的解释。因此,从IDE的角度来看,我们期待更快的运行时速度,或者更少的模棱两可,或者两者兼而有之?

Mostly the former.

主要是前者。

Just a note that methods are not statically dispatched if you refer the struct via a protocol.

只需注意,如果通过协议引用结构,则方法不是静态调度的。

NOT safe in a multi-threaded environment If the instance passed as object is being accessed by multiple threads simultaneously, this function may still return true. Therefore, you must only call this function from mutating methods with appropriate thread synchronization. That will ensure that isKnownUniquelyReferenced(_:) only returns true when there is really one accessor, or when there is a race condition, which is already undefined behavior.

在多线程环境中不安全如果作为对象传递的实例被多个线程同时访问,则此函数仍可能返回TRUE。因此,您只能通过适当的线程同步从变异方法中调用此函数。这将确保isKnownUniquelyReferated(_:)仅在真正只有一个访问者时返回True,或者当存在竞争条件时返回True,这已经是未定义的行为。

you never created an instance of FLightB

您从未创建过FlightB的实例

then why are you talking about FlightB bro? Here we can see that the FlightB instance is not changed

那你为什么要谈论FlightB兄弟?在这里,我们可以看到FlightB实例没有更改

@ManojKarki, great answer. Just wanted to point out that you declared flightA twice when I think you wanted to declare FlightA, then FlightB.

@ManojKarki,很棒的回答。只是想指出,你声明了两次航班A,我认为你想声明航班A,然后是航班B。

Could you provide some links from what you mentioned? Cause on WWDC there are quite a few to choose from, I'd like to watch the one talking about this specific topic

你能提供一些你提到的链接吗?因为在WWDC上有相当多的选择,我想看一看谈论这个特定主题的那个

For me this is a good start here: github.com/raywenderlich/…

对我来说,这是一个很好的开始:githeb.com/raywenderlich/…

He's probably talking about this great session: Protocol-Oriented Programming in Swift. (Links: video, transcript)

他可能是在谈论这个伟大的会议:SWIFT中的面向协议编程。(链接:视频,文字记录)

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com