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

评论

在代码中传递结构时,总是将其复制,并且不使用引用计数。来源:developer.apple.com/library/prerelease/ios/documentation/swift / ...

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

在整个对话中,我感到很惊讶,没有直接提到写时复制又称“懒惰”副本。由于这种设计,对结构复制性能的任何担忧都几乎没有意义。

选择一个类的结构不是意见问题。选择其中一个有特定的原因。
我强烈建议您查看为什么Array不是threadSafe。这是相关的,因为数组和结构都是值类型。这里的所有答案都提到,使用structs / arrays / value类型永远不会出现线程安全问题,但是有一个极端的情况。

#1 楼

根据非常流行的WWDC 2015演讲中的Swift面向协议编程(视频,成绩单),Swift提供了许多功能,这些功能在许多情况下都使结构比类更好。

结构相对较可取小型且可复制,因为与在类中进行多次引用相比,对同一实例进行多次引用比复制更安全。在将变量传递给许多类和/或在多线程环境中时,这一点尤其重要。如果您始终可以将变量的副本发送到其他地方,则不必担心其他地方会更改您下面的变量的值。

使用Structs,您不必担心太多了有关内存泄漏或多个线程竞相访问/修改变量的单个实例的信息。 (从更专业的角度来说,例外是在闭包内部捕获结构时,因为除非您将其明确标记为要复制,否则它实际上是在捕获对实例的引用)。

类可以也变得肿,因为一个类只能从单个超类继承。这鼓励我们创建巨大的超类,其中包含了许多松散相关的不同能力。使用协议,尤其是使用协议扩展(可以为协议提供实现),可以消除类对实现这种行为的需要。

提出了优先选择类的这些方案: br />


复制或比较实例没有意义(例如Window)
实例寿命与外部效果(例如TemporaryFile)有关
实例仅仅是“接收器”-到外部状态(例如CGContext)的只写管道。



这意味着结构应该是默认的,而类应该是后备的。 br />
另一方面,Swift编程语言文档有些矛盾:


结构实例始终按值传递,而class
实例始终按引用传递。这意味着它们适合于各种任务。在考虑项目所需的数据构造和功能时,请确定
是将每个数据构造定义为类还是定义为
结构。

作为一般准则,请考虑在满足以下一个或多个条件的情况下创建结构:


结构的主要目的是封装一些相对简单的数据值。 br />可以合理地预期,当您分配或传递该结构的
实例时,将复制而不是引用封装的值。
该结构存储的任何属性本身就是值类型,这些值类型
该结构不需要继承其他现有类型的属性或行为。

结构良好候选对象的示例包括:


几何形状的大小,也许封装了width属性和height属性,都是Double类型。
引用系列中范围的一种方法,可能封装了Int类型的start属性和length属性。
3D坐标系中的一个点,可能封装了x, y和z属性,每个属性均为Double类型。

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


在这里,我们声称我们应该默认仅在特定情况下使用类和结构。最终,您需要了解值类型与引用类型在现实世界中的含义,然后可以就何时使用结构或类做出明智的决定。另外,请记住,这些概念一直在发展,并且在进行面向协议的编程演讲之前编写了Swift编程语言文档。

评论


@ElgsQianChen本文的重点是默认情况下应选择struct,并且仅在必要时使用class。结构更加安全且无漏洞,尤其是在多线程环境中。是的,您始终可以使用类来代替结构,但最好使用结构。

–drewag
2014年9月22日上午8:22

@drewag这似乎与它所说的完全相反。有人说,类应该是您使用的默认值,而不是结构。实际上,这意味着大多数自定义数据构造应该是类,而不是结构。您能否向我解释一下,看完之后如何获得大多数数据集应该是结构而不是类?当某些东西应该是一个结构时,他们给出了一套特定的规则,并且几乎说“在所有其他情况下,一个班级会更好”。

–马特
14-10-3在4:42

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

–丹·罗森斯塔克(Dan Rosenstark)
15年1月16日在21:47

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

–大卫·詹姆斯(David James)
15年12月4日在19:53

在类上构造绝对可以降低复杂性。但是当结构成为默认选择时,对内存使用的影响是什么。当事物被复制到各处而不是被引用时,应会增加应用程序的内存使用量。不是吗

– MadNik
16-10-11在5:37



#2 楼

由于结构实例是在堆栈上分配的,而类实例是在堆上分配的,因此结构有时有时会更快。

但是,您应该始终对其进行度量并根据自己的独特用例进行决定。

考虑以下示例,该示例演示了使用Intstruct包装class数据类型的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

UPDATE(2018年3月27日)中找到:

从Swift 4.0开始,Xcode 9.2,在iPhone 6S,iOS 11.2.6上运行Release版本,Swift编译器设置为-O -whole-module-optimization



class版本耗时2.06秒

struct版本花费了4.17e-08秒(快了50,000,000倍)

(由于方差很小,在5%以下,我不再平均多次运行)

注意:如果不对整个模块进行优化,差异就不那么明显了。如果有人能指出该标志的实际作用,我会感到非常高兴。


UPDATE(2016年5月7日):

从Swift 2.2.1开始, Xcode 7.3,正在iPhone 6S,iOS 9.3.1上运行Release版本,平均运行5次,Swift编译器设置为-O -whole-module-optimization



class版本耗用了2.159942142s

struct版本花费了5.83E-08s(快了37,000,000倍)

注意:正如有人提到的那样,在现实情况下,结构中可能会有多个字段,已为具有10个字段而不是1个字段的结构/类添加了测试。令人惊讶的是,结果变化不大。


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

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

从Swift 1.2开始,Xcode 6.3.2在iPhone 5S,iOS 8.3上运行Release版本,平均运行5次以上



class版本花费了9.788332333s

struct版本花费了0.010532942s(快了900倍)


旧结果(来自未知时间)

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

在MacBook Pro上发布版本:


class版本耗时1.10082秒
struct版本耗时0.02324秒(快50倍)


评论


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

– Tylerc230
15年2月26日在7:00

-1这个测试不是一个很好的例子,因为结构上只有一个var。请注意,如果添加多个值和一个或两个对象,则结构版本将与类版本相当。您添加的var越多,结构版本越慢。

– joshrl
2015年6月2日14:07



@joshrl是您的意思,但是一个例子是“好”还是不取决于具体情况。该代码是从我自己的应用程序中提取的,因此这是一个有效的用例,使用结构确实极大地提高了我的应用程序的性能。这可能不是一个常见的用例(嗯,对于大多数应用程序,常见的用例是没有人关心数据的传输速度如何,因为瓶颈发生在其他地方,例如网络连接,无论如何,优化并不是那样。当您拥有配备GB或RAM的GHz设备时,这一点至关重要。

–庆阮(Khanh Nguyen)
2015年6月3日在1:59



据我了解,快速复制已优化为在WRITE时进行。这意味着除非要更改新副本,否则不会进行物理内存副本。

–马特扬
16年4月11日在18:48

这个答案显示了一个极其琐碎的例子,以至于不切实际,因此在许多情况下是不正确的。更好的答案是“取决于情况”。

–被抢劫
16年5月21日在19:29

#3 楼

结构和类之间的相似之处。
我通过简单的示例为此创建了要点。
https://github.com/objc-swift/swift-classes-vs-structures
和差异
/> 1。继承。
结构不能迅速继承。如果要
class Vehicle{
}

class Car : Vehicle{
}

上课。
2。通过By
Swift结构通过值传递,而类实例通过引用传递。
上下文差异
结构常量和变量
示例(在WWDC 2014上使用)
struct Point{
 
   var x = 0.0;
   var y = 0.0;

} 

/>定义一个名为Point的结构。
var point = Point(x:0.0,y:2.0)

现在,如果我尝试更改x。它是一个有效的表达式。
point.x = 5

但是如果我将一个点定义为常量。
let point = Point(x:0.0,y:2.0)
point.x = 5 //This will give compile time error.

在这种情况下,整个点都是不可变的常量。
如果我使用一个类取而代之,这是一个有效的表达式。因为在类中,不可变常量是对类本身的引用,而不是对其实例变量的引用(除非那些变量定义为常量)

评论


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

– thatguy
2015年9月13日在16:25



上面的要点是关于如何实现struct的继承风格。您将看到类似的语法。 A:B。它是称为A的结构,实现了称为B的协议。Apple文档明确提到该结构不支持纯继承,但不支持纯继承。

– MadNik
15年9月16日在12:43

你的最后一段很棒。我一直都知道您可以更改常量...但是有时我会发现您无法更改常量,所以我感到困惑。这种区别使其可见

–蜂蜜
17年8月16日在11:42

#4 楼

假设我们知道Struct是值类型,而Class是引用类型。

如果您不知道值类型和引用类型是什么,那么请参阅按引用传递与。按值传递?

基于mikeash的帖子:


...让我们首先看一些极端的,显而易见的例子。整数显然是可复制的。它们应该是值类型。无法明智地复制网络套接字。它们应该是引用类型。 x,y
对中的点是可复制的。它们应该是值类型。不能合理地复制代表磁盘的控制器。那应该是一个引用类型。

某些类型可以被复制,但它可能并不是您想一直发生的。这表明它们应该是引用
类型。例如,可以从概念上复制屏幕上的按钮。
副本与原始副本不会完全相同。单击
副本将不会激活原始副本。该副本将不会在屏幕上占据相同的位置。如果将按钮传递给周围或放入新变量中,则可能要引用原始按钮,而
仅在显式请求时才创建副本。
意味着您的按钮类型应该是引用类型。

视图和窗口控制器是一个类似的示例。可以想象,它们可能是可复制的,但几乎从来都不是您想要的。
它们应该是引用类型。

模型类型呢?您可能具有代表系统上用户
的用户类型,或代表了
用户执行的操作的犯罪类型。这些都是可复制的,因此它们可能应该是value
类型。但是,您可能希望对该程序的其他部分可见的在程序中某个位置进行的用户犯罪更新。
这表明您的用户应由某种类型的user
控制器进行管理,这将是一种引用类型。例如,

struct User {}
class UserController {
    var users: [User]

    func add(user: User) { ... }
    func remove(userNamed: String) { ... }
    func ...
}


集合是一个有趣的案例。这些包括诸如数组
和字典之类的东西,以及字符串。它们可以复制吗?明显。是
复制您想轻松且经常发生的事情吗?
不太清楚。

大多数语言对此表示“否”,并使它们的集合引用
类型。在Objective-C和Java,Python和JavaScript
以及我能想到的几乎所有其他语言中都是如此。 (一个主要的例外情况是
是具有STL集合类型的C ++,但是C ++是
语言界疯狂的疯子,它做的一切都很奇怪。)

斯威夫特说“是”,其中表示像Array和Dictionary和
String这样的类型是结构而不是类。它们在赋值时被复制,
并在作为参数传递时被复制。只要副本便宜,这是一个完全明智的选择,Swift会非常努力地做到。
...


我个人不要这样命名我的班级。我通常将我的UserManager命名为UserManager,而不是UserController。

此外,当您必须重写函数的每个实例(即它们没有任何共享功能)时,请不要使用类。 br />
因此,不要有一个类的几个子类。使用几个符合协议的结构。


结构的另一个合理情况是,当您想对旧模型和新模型进行增量/差异处理时。对于引用类型,您不能开箱即用。使用值类型时,不会共享突变。

评论


正是我想要的那种解释。尼斯写:)

– androCoder-BD
18/12/31在15:41

非常有用的控制器示例

–问P
2月24日5:05

@AskP我给迈克自己发了电子邮件,并获得了额外的代码:)

–蜂蜜
2月24日10:53

#5 楼

还有其他一些需要考虑的原因:



结构获得了一个自动初始化程序,而您根本不需要在代码中进行维护。

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



要在一个类中使用它,您将必须添加初始化程序,并维护初始化程序...



>像Array这样的基本集合类型是结构。您在自己的代码中使用它们的次数越多,您就会习惯于按值传递而不是引用。例如:

func removeLast(var array:[String]) {
   array.removeLast()
   println(array) // [one, two]
}

var someArray = ["one", "two", "three"]
removeLast(someArray)
println(someArray) // [one, two, three]


显然,不变性与可变性是一个巨大的话题,但是许多聪明人认为,不变性(在这种情况下为结构)是更可取的。可变对象与不可变对象


评论


的确,您会获得自动初始化程序。当所有属性均为Optional时,您还将获得一个空的初始化程序。但是,如果您在Framework中有一个结构,则希望自己在内部范围之外使用它,就需要自己编写初始化程序。

– Abizern
16 Mar 11 '16 at 13:46

@Abizern确认-stackoverflow.com/a/26224873/8047-和那个讨厌的人。

–丹·罗森斯塔克(Dan Rosenstark)
16 Mar 11 '16 at 22:26

@Abizern在Swift中进行所有操作都有很大的理由,但是每次在某个地方而不是在另一个地方都是正确的事情时,开发人员必须了解更多的知识。我猜这是我应该说的地方:“以如此具有挑战性的语言工作真是令人兴奋!”

–丹·罗森斯塔克(Dan Rosenstark)
16-3-12的1:13

我还可以补充一点,不是让结构有用的不是结构的不变性(尽管这是一件非常好的事情)。您可以变异结构,但是必须将方法标记为变异,这样您就可以清楚地知道哪些函数会改变它们的状态。但是它们作为值类型的性质很重要。如果使用声明结构,则不能对其调用任何变异函数。关于通过值类型更好地编程的WWDC 15视频是一个很好的资源。

– Abizern
16-3-12在11:21

感谢@Abizern,在阅读您的评论之前,我从未真正理解过此内容。对于对象,let vs. var并没有太大的区别,但是对于结构而言,它是巨大的。感谢您指出了这一点。

–丹·罗森斯塔克(Dan Rosenstark)
16 Mar 15 '16 at 20:47

#6 楼

一些优点:


由于不可共享而具有自动线程安全性
由于没有isa和refcount(实际上通常是堆栈分配的),因此使用较少的内存。
方法是总是静态调度的,因此可以内联(尽管@final可以对类执行此操作)
相同的理由(无需像NSArray,NSString等那样典型地进行“防御性复制”)相同线程安全的原因


评论


不知道这是否超出此答案的范围,但是您能否解释(或链接,我想)“方法总是静态分配的”这一点?

–丹·罗森斯塔克(Dan Rosenstark)
16-3-12的1:14

当然。我也可以附加警告。动态分派的目的是在您不知道要使用哪个实现时选择一种实现。在Swift中,这可能是由于继承(可能在子类中重写),也可能是由于函数是通用的(您不知道通用参数是什么)。无法继承结构,并且整个模块优化+泛型专门化大都消除了未知的泛型,因此可以直接调用方法,而不必查找要调用的内容。虽然,非专业的泛型仍然对结构进行动态分配

–Catfish_Man
16年3月12日在6:37

谢谢,很好的解释。因此,我们期望更高的运行速度,或者从IDE角度看减少歧义,或者两者兼而有之?

–丹·罗森斯塔克(Dan Rosenstark)
16 Mar 15 '16 at 20:48

主要是前者。

–Catfish_Man
16 Mar 15 '16在23:10

请注意,如果您通过协议引用该结构,则方法不会静态分配。

– Cristik
18年1月4日在7:10

#7 楼

结构比Class快得多。另外,如果需要继承,则必须使用Class。最重要的一点是,Class是引用类型,而Structure是值类型。例如,

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  
}


现在让我们创建两者的实例。

var flightA = Flight()

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


现在让我们将这些实例传递给两个函数可修改ID,描述,目的地等。

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




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)


现在,如果我们打印FlightA的ID和说明,我们将得到

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


这里,我们可以看到FlightA的ID和描述已更改,因为传递给Modify方法的参数实际上指向了FlightA对象(引用类型)的内存地址。

现在我们是否打印FLightB的ID和描述我们得到的实例

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


在这里我们可以看到FlightB实例未更改,因为在ModifyFlight2方法中,Flight2的实际实例是传递而不是引用(值类型) 。

评论


您从未创建FLightB的实例

– David Seek
16-10-19在0:34

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

– David Seek
16-10-19在14:47

@ManojKarki,好答案。我只想指出,当我想声明FlightA,然后声明FlightB时,您两次声明了flightA。

–ScottyBlades
17年8月26日在1:14

#8 楼

Structsvalue typeClasses


值类型比引用类型快
值类型实例在多线程环境中是安全的,因为
多线程可以变异实例而不必担心竞争条件或死锁
值类型与引用类型不同,没有引用;因此,没有内存泄漏。

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


您希望副本具有独立状态,则将使用数据在多个线程中的代码中

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


要创建共享的可变状态。

其他信息也可以在Apple文档中找到

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


其他信息

快速值类型保存在堆栈中。在一个进程中,每个线程都有自己的堆栈空间,因此,其他线程将无法直接访问您的值类型。因此,没有竞争条件,锁,死锁或任何相关的线程同步复杂性。值类型不需要动态内存分配或引用计数,这都是昂贵的操作。同时,值类型的方法是静态分派的。这些在性能方面有利于使用值类型。

这里提醒一下,这里是Swift

值类型的列表:


结构
枚举
元组
原语(整数,双精度,布尔等)
集合(数组,字符串,字典,集合)

引用类型:



来自NSObject的所有内容
函数
关闭


#9 楼

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


使用值类型[例如, struct,enum]时间:


将实例数据与==进行比较很有意义
您希望副本具有独立的状态
数据将在多个代码中使用线程

使用引用类型[例如类]何时:


将实例身份与===进行比较很有意义
您要创建共享的可变状态



如该文章中所述,没有可写属性的类的行为与结构相同,但有一个警告:(结构)最适合线程安全模型-现代应用程序体系结构中的需求日益迫切。

#10 楼

使用类,您可以继承并通过引用传递,而结构则没有继承,而是通过值传递。

Swift上有许多很棒的WWDC会话,其中一个具体问题得到了详细解答。 。请确保您观看这些内容,因为它可以使您比语言指南或iBook更快地掌握速度。

评论


您能否提供您提到的链接?因为WWDC上有很多可供选择,我想看一个谈论这个特定主题的人

– MMachinegun
2015年2月4日14:59

对我来说,这是一个不错的开始:github.com/raywenderlich/…

– MMachinegun
15年2月4日在15:58

他可能正在谈论这个伟大的会议:Swift中的面向协议的编程。 (链接:视频,成绩单)

– zekel
16年3月3日在18:55

#11 楼

我不会说结构提供的功能较少。

当然,除了可变函数外,self是不可变的,仅此而已。

只要您继承成功坚持旧的好主意,即每个类都应该是抽象的或最终的。

将抽象类实现为协议,将最终类实现为结构。

关于结构的优点是您可以在不创建共享可变状态的情况下使字段可变,因为写时复制可解决此问题:)

这就是为什么以下示例中的属性/字段都是可变的,而我在Java中不会这样做或C#或swift类。

示例继承结构在名为“ 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)")
}


#12 楼


在Swift中,引入了一种新的编程模式,称为面向协议的编程。


创建模式:

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

类是引用类型,在分配过程中不会自动克隆它。要实现原型模式,类必须采用NSCopying协议。


浅表副本仅复制指向该对象的引用,而深层副本则复制对象的引用。


为每种引用类型实现深拷贝已成为一项繁琐的任务。如果类还包含其他引用类型,则必须为每个引用属性实现原型模式。然后我们必须通过实现NSCopying协议来实际复制整个对象图。

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

class Address{
   var street:String
   ...
} 


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

#13 楼

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

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

#14 楼

在这些答案中没有引起注意的一点是,持有类与结构的变量可以是let,同时仍允许更改对象的属性,而不能使用结构进行此操作。

这是如果您不希望变量指向另一个对象,但仍然需要修改该对象,则很有用,例如,在有许多实例变量想要一次又一次更新的情况下。如果是结构,则必须使用var将该变量完全重置为另一个对象,以执行此操作,因为Swift中的常量值类型正确地允许零突变,而引用类型(类)的行为不这样。

#15 楼

由于struct是值类型,您可以很容易地创建存储在堆栈中的内存.Struct可以轻松访问,并且在工作范围之后,可以很容易地从堆栈内存中通过弹出栈顶部的pop来重新分配它。
另一方面,类是一个引用类型,它存储在堆中,并且在一个类对象中所做的更改将紧密地耦合到另一个对象,从而影响其他对象和引用类型。结构的所有成员都是公共的,而类的所有成员都是私有的。

struct的缺点是它不能被继承。

#16 楼


结构和类是用户定义的数据类型
默认情况下,结构是公共的,而类是私有的
类实现封装的原理
类的对象在堆上创建内存
用于可重用性,而结构用于对
相同结构中的数据进行分组
结构数据成员不能直接初始化,但可以由外部分配结构
类数据成员可以直接通过参数less
构造函数进行初始化,并通过参数化的构造函数进行分配


评论


有史以来最差的答案!

– J. Doe
18年11月13日在13:38

复制粘贴答案

–贾瓦德·阿里(Jawad Ali)
19/12/16在4:54