swift语言中的结构与类

从苹果书 “结构和类之间最重要的区别之一是,结构在代码中传递时总是被复制,但类是通过引用传递的。”< / p >

有人能帮我理解一下这是什么意思吗?对我来说,类和结构似乎是一样的。

86614 次浏览

下面是一个带有class的例子。请注意,当名称更改时,两个变量引用的实例将如何更新。Bob现在是Sue,在任何Bob被引用的地方。

class SomeClass {
var name: String
init(name: String) {
self.name = name
}
}


var aClass = SomeClass(name: "Bob")
var bClass = aClass // aClass and bClass now reference the same instance!
bClass.name = "Sue"


println(aClass.name) // "Sue"
println(bClass.name) // "Sue"

现在使用struct,我们看到值被复制,每个变量保留自己的值集。当我们将名称设置为Sue时,aStruct中的Bob结构体不会被更改。

struct SomeStruct {
var name: String
init(name: String) {
self.name = name
}
}


var aStruct = SomeStruct(name: "Bob")
var bStruct = aStruct // aStruct and bStruct are two structs with the same value!
bStruct.name = "Sue"


println(aStruct.name) // "Bob"
println(bStruct.name) // "Sue"

因此,对于表示有状态的复杂实体,class非常棒。但对于仅仅是测量值或相关数据位的值,struct更有意义,这样你就可以轻松地复制它们并使用它们计算或修改值,而不用担心副作用。

struct是值类型。这意味着,如果你将结构的实例复制到另一个变量,它只是复制到变量。

值类型示例

struct Resolution {
var width = 2
var height = 3
}


let hd = Resolution(width: 1920, height: 1080)
var cinema = hd //assigning struct instance  to variable
println("Width of cinema instance is \(cinema.width)")//result is 1920
println("Width of hd instance is \(hd.width)")//result is 1920


cinema.width = 2048


println("Width of cinema instance is \(cinema.width)")//result is 2048
println("Width of hd instance is \(hd.width)")//result is 1920

类是引用类型。这意味着如果你将类的一个实例分配给一个变量,它将只保留参考实例和不是副本

如果你再往下看苹果手册,你会看到这部分: "结构和枚举是值类型"

在本节中,你会看到:

< p >“让高清=决议(宽度:1920,身高:1080)var 这个例子声明了一个名为hd的常量,并设置了它 初始化为full的宽和高的Resolution实例 高清视频(1920像素宽,1080像素高)

然后声明一个名为cinema的变量,并将其设置为当前值 hd的值。因为决议是一种结构,是现有的副本 实例,并将此新副本分配给cinema。尽管 Hd和cinema现在有相同的宽和高,它们是两个 幕后完全不同的实例。

属性的width属性被修改为 用于数字电影放映的稍宽的2K标准(2048 像素宽和1080像素高):

电影。检查影院显示的width属性 它确实已经改变为2048:

< p > println(“现在电影(电影。宽度)像素宽”)/ / 的width属性现在是2048像素宽 原来的hd实例仍然有1920的旧值:

println ("hd is still (hd . width) pixels wide") //打印"hd . width " 仍然是1920像素宽"

当cinema被赋予hd的当前值时,值存储在hd中 复制到新的影院实例中。最终结果是2 完全独立的实例,恰好包含相同的实例 数值。因为它们是单独的实例,设置宽度

摘自:苹果公司的“快速编程语言。“iBooks。 https://itun.es/us/jEUH0.l < / p >

这是结构体和类之间最大的区别。复制结构,引用类。

通常(在大多数编程语言中),对象是存储在堆上的数据块,然后对这些块的引用(通常是指针)包含用于访问这些数据块的name。这种机制允许通过复制对象引用(指针)的值来共享堆中的对象。对于基本数据类型(如integer)则不是这样,这是因为创建引用所需的内存几乎与对象相同(在本例中为整数值)。因此,在大对象的情况下,它们将作为值传递而不是作为引用。

Swift使用struct来提高String和Array对象的性能。

这是一本很好的读物

类和结构都可以做到:

  • 定义属性来存储值
  • 定义提供功能的方法
  • 被扩展
  • 遵守协议
  • 定义初始化
  • 定义下标以提供对其变量的访问

唯一的类可以做到:

  • 继承
  • 铸字
  • 定义deinitialisers
  • 允许多个引用进行引用计数。

下面是一个例子,它精确地显示了struct和class之间的区别。

< p > 在操场上写的代码的截图 < br > 游乐场代码截图 < / p >
struct Radio1{
var name:String
//    init(name:String) {
//        self.name = name
//    }
}


struct Car1{
var radio:Radio1?
var model:String


}


var i1 = Car1(radio: Radio1(name:"murphy"),model:"sedan")
var i2 = i1
//since car instance i1 is a struct and
//this car has every member as struct ,
//all values are copied into i2


i2.radio?.name //murphy
i2.radio = Radio1(name: "alpha")
i2.radio?.name //alpha


i1.radio?.name //murphy


//since Radio1 was struct ,
//values were copied and thus
// changing name  of instance of Radio1 in i2
//did not bring change in i1


class Radio2{
var name:String
init(name:String) {
self.name = name
}
}


struct Car2{
var radio:Radio2?
var model:String


}
var i3 = Car2(radio: Radio2(name:"murphy"),model:"sedan")
//var radioInstance = Radio2(name: "murphy")
//var i3 = Car2(radio: radioInstance,model:"sedan")


var i4 = i3
//since i3 is instance of struct
//everything is copied to i4 including reference of instance of Radio2
//because Radio2 is a class






i4.radio?.name //murphy
i4.radio?.name="alpha"
i4.radio?.name //alpha


i3.radio?.name //alpha


//since Radio2 was class,
//reference was copied and
//thus changing name of instance
//of Radio2 in i4 did  bring change in i3 too




//i4.radio?.name
//i4.radio = Radio2(name: "alpha")
//i4.radio?.name
//
//i3.radio?.name

以上答案都是正确的,我希望我的回答能帮助到那些不理解以上答案的人。

在Swift中有两种类型的对象

  1. 结构体

它们之间的主要区别是

  • Struct是价值类型
  • 类的类型是参考

例如这里的代码要理解得很好。

struct SomeStruct {
var a : Int;


init(_ a : Int) {
self.a = a
}
}


class SomeClass {
var a: Int;


init(_ a: Int) {
self.a = a
}


}
var x = 11


var someStruct1 = SomeStruct(x)
var someClass1 = SomeClass(x)


var someStruct2 = someStruct1
var someClass2 = someClass1


someClass1.a = 12
someClass2.a // answer is 12 because it is referencing to class 1     property a


someStruct1.a = 14
someStruct2.a // answer is 11 because it is just copying it not referencing it

这是主要的区别,但我们也有次要的区别。

  1. 必须声明初始化式(构造函数)
  2. 有deinitialisers
  3. 可以从其他类继承吗

结构体

  1. 它为你提供了自由初始化器,你不必声明初始化器,如果你声明了自由初始化器,它将被你声明的初始化器覆盖
  2. 没有去初始化
  3. 不能从其他结构继承

为了理解struct和class之间的区别,我们需要知道值类型和引用类型之间的主要区别。struct是值类型,这意味着对它们的每一次更改都只会修改该值,类是引用类型,引用类型中的每一次更改都将修改分配在内存或引用位置的值。例如:

让我们从一个类开始,这个类符合Equatable,只是为了能够比较实例,我们创建了一个名为__abc0的实例和另一个名为pointClassInstanceB的实例,我们将类a分配给类B,现在断言说它们是相同的…

class PointClass: Equatable {
var x: Double
var y: Double


init(x: Double, y: Double) {
self.x = x
self.y = y
}


static func == (lhs: PointClass, rhs: PointClass) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
}


var pointClassInstanceA = PointClass(x: 0, y: 0)
var pointClassInstanceB = pointClassInstanceA


assert(pointClassInstanceA==pointClassInstanceB)


pointClassInstanceB.x = 10
print(pointClassInstanceA.x)
//this prints 10

好的,这里发生了什么为什么如果我们改变了pointclassinstanceb的x值它也改变了pointClassInstanceA的x值?这展示了引用类型是如何工作的,当我们将实例A赋值为实例B的值,然后我们修改其中一个的X,它会改变两个X,因为它们共享相同的引用,而改变的是该引用的值。

让我们用结构体做同样的事情

struct PointStruct: Equatable {
var x: Double
var y: Double


init(x: Double, y: Double) {
self.x = x
self.y = y
}


static func == (lhs: PointStruct, rhs: PointStruct) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
}
var pointStructInstanceA = PointStruct(x: 0, y: 0)
var pointStructInstanceB = pointStructInstanceA


assert(pointStructInstanceA==pointStructInstanceB)
pointStructInstanceB.x = 100
print(pointStructInstanceA.x)
//this will print 0

我们的结构与我们的类基本相同,但现在你可以看到,当你打印pointStructInstanceA的x值时,它没有改变,这是因为值类型的工作方式不同,它们的一个实例上的每一个变化都是“独立的”,不会影响到另一个实例。

Swift建议使用更多的值类型,你可以看出他们的库是基于结构的,以避免引用类型带来的问题,比如无意中修改一个值等。结构是斯威夫特的发展方向。

已经有很多关于这方面的文章了,我想在这里加一个类比。希望你看完这篇文章后,心中永远不会再有疑虑: 底线: 类通过引用传递,而结构通过值传递

假设你和你的朋友共享一个谷歌文档表。现在,如果他改变了其中的任何内容,你也会在谷歌文档上看到变化,这意味着你的副本也受到了影响。 这基本上是"通过引用"

但是假设,如果你有一个。xls文件保存在你的机器里。你把那份文件交给你的朋友。现在,如果他在那个文件中做了任何更改,你的文件也不会被打乱/影响,因为你有自己的副本。 这基本上是“通过值传递”。 你已经有多个简单的程序来检查这个类比在swift操场上

这个问题似乎是重复的,但无论如何,下面的问题将回答大多数用例:

  1. 结构和类之间最重要的区别之一 结构是值类型并且总是被复制吗 在您的代码中传递,类是引用类型和 通过引用传递。李< / p > < / > 类也有继承,它允许一个类继承 另一个特征。

  2. 结构属性存储在堆栈中,类实例存储在堆栈中 因此,有时堆栈比a快得多 李课。< / p > < / >

  3. Struct自动获得默认初始化式,而在Class中,我们 必须初始化。

  4. Struct在任何时候都是线程安全的或单例的。

< p >, 要总结结构和类之间的区别,有必要了解值类型和引用类型之间的区别
  1. 当你复制一个值类型时,它从 你要复制到新变量中的东西。它们是分开的 事物和改变一个并不影响另一个
  2. 复制引用类型时,新变量引用 与你要复制的东西相同的内存位置。这意味着 改变一个会改变另一个因为它们都指向 相同的内存位置。 下面的示例代码可以作为参考

/ / sampleplayground.playground

  class MyClass {
var myName: String
init(myName: String){
self.myName = myName;
}
}


var myClassExistingName = MyClass(myName: "DILIP")
var myClassNewName = myClassExistingName
myClassNewName.myName = "John"




print("Current Name: ",myClassExistingName.myName)
print("Modified Name", myClassNewName.myName)


print("*************************")


struct myStruct {
var programmeType: String
init(programmeType: String){
self.programmeType = programmeType
}
}


var myStructExistingValue = myStruct(programmeType: "Animation")
var myStructNewValue = myStructExistingValue
myStructNewValue.programmeType = "Thriller"


print("myStructExistingValue: ", myStructExistingValue.programmeType)
print("myStructNewValue: ", myStructNewValue.programmeType)

输出:

Current Name:  John
Modified Name John
*************************
myStructExistingValue:  Animation
myStructNewValue:  Thriller
1.structure is value type.
= > when we assign structure variable to other variable or pass as parameter to function, it creates separate/new copy => so that changes made on one variable does not  reflect on another.[We can say like **call by value** concept]
Example :


struct DemoStruct
{
var value: String
init(inValue: String)
{
self.value = inValue
}
}




var aStruct = DemoStruct(inValue: "original")
var bStruct = aStruct // aStruct and bStruct are two structs with the same value! but references to diff location`enter code here`
bStruct.value = "modified"


print(aStruct.value) // "original"
print(bStruct.value) // "modified"




2.class is reference type.
= > when we assign structure variable to other variable or pass as parameter to function, it **does not** creates separate/new copy => so that changes made on one variable does not  reflect on another.[We can say like **call by reference** concept]
Example:
class DemoClass
{
var value: String
init(inValue: String)
{
self.value = inValue
}
}


var aClass = DemoClass(inName: "original")
var bClass = aClass // aClass and bClass now reference the same instance!
bClass.value = "modified"


print(aClass.value) // "modified"
print(bClass.value) // "modified"

斯威夫特类型

  • named typenominal typetype with name
  • compound typenon-nominal typetype without name

Value type是一种类型,其值在赋值给变量或常量、传递给函数或从函数返回时被复制。(同时asis检查会生成struct的副本)

Reference types在赋值给变量或常量或传递给函数时不会被复制

值类型:

StructEnum<一口>[对]< /一口>Tuple
struct Stringstruct Array(SetDictionary)

(objective - c int…)

字符串,内置集合值类型包含内部参考heap来管理它的大小

  • 当你分配或传递 value type时,一个新的数据复制被创建。copy on write - COW机制用于一些特定的类(如Collections(Array, Dictionary, Set)),并进行了一些优化,例如当对象被修改时创建副本。对于自定义类型,您应该自己支持COW
  • 当你修改一个实例时,它只有当地的效果。
  • 如果Value为局部变量,则使用栈内存
< p > 引用类型:
Class, Function < / p >

(Objective-C所有其他)

ARC被使用

  • 当你分配或传递reference type时,一个新参考将被创建给原始实例(实例的地址被复制)。
  • 当你修改一个实例时,它具有全球效应,因为该实例可以被指向它的任何引用共享和访问。
  • 通常使用堆内存

enter image description here

Value type建议使用默认情况下Value type的最大优点是它们通常是thread safe

Reference type优点:

  • 它们可以遗传,
  • deinit()可以被使用,
  • 通过引用===比较实例
  • Objective-C互操作性,因为Swift中引入了Value Type
< p > [堆栈vs堆]
[let vs var, class vs struct]
[类别vs结构]
< / p > < p > 在结构和类之间选择
类型
类和结构 < / p >

正如许多人已经指出复制结构体和类的区别一样,这可以从它们在c语言中的来源来理解,像这样的结构体

struct A {
let a: Int
let c: Bool
}

在func父对象或结构体的局部内存中,它将是这样的

64bit for int
8 bytes for bool

现在

class A {
let a: Int
let c: Bool
}

而不是存储在本地内存或结构或类中的数据内容,它将是一个单一指针

64bit address of class A instance

当你复制这两个时,很容易看出为什么会有区别,复制第一个,你复制了64位的int和8位的bool,复制第二个,你复制了64位的地址到A类的实例,你可以有同一个内存地址的多个副本,都指向同一个实例,但结构体的每个副本都将是它自己的副本。

现在事情变得复杂了因为你可以把这两个混合起来

struct A {
let a: ClassA
let c: Bool
}

你的记忆会是这样的

64bit address of class A instance
8 bytes for bool

这是一个问题,因为即使你在程序中有多个结构的副本,它们都有一个副本到同一个对象ClassA,这意味着就像你传递的多个引用到实例ClassA必须有一个引用计数,记录对象存在多少个引用,以便知道何时删除它们,你的程序可以有多个引用到结构a,需要对它们的ClassA实例保持引用计数,如果你的结构体中有很多类,或者它包含的结构体中有很多类,这可能会很耗时,现在当你复制你的结构体时,编译器必须生成代码,遍历你的结构体和子结构中引用的每个类实例,并增加引用计数来跟踪有多少引用。这可以使类传递得更快,因为你只需要复制它的单个地址,并且它不需要增加任何子类的引用计数,因为它想要减少它所包含的任何子类的引用计数,直到它自己的引用计数达到0。

一些苹果的事情变得更复杂结构类型,他们实际上有对象类型,数据引用的好处,它可以存储在内存中,延长和承包商,他们可以是非常大的,与数据存储在本地堆栈,所以类型如字符串、数组、集合,字典虽然他们像结构体,甚至会重复的内部数据如果你想修改它们,这样你就不会改变发生,一切这些数据仍然需要进行引用计数,因此包含大量这些类型的结构仍然可能很慢,因为必须保留每种类型的内部数据。

当然,传递结构类型可以减少大量错误的可能性,但它们也会降低程序的速度,这取决于所包含的类型。