快速语言中的结构与类

时间:2014-06-14 07:18:32

标签: swift

来自Apple的书 “结构和类之间最重要的区别之一是结构总是在代码中传递时被复制,但是类通过引用传递。”

任何人都可以让我理解这意味着什么,对我而言,类和结构似乎是一样的。

13 个答案:

答案 0 :(得分:441)

以下是class的示例。请注意,如果更改名称,则更新两个变量引用的实例。 <{1}}现在BobSue被引用的所有地方。

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中的Bob结构不会发生变化。

aStruct

因此,为了表示有状态的复杂实体,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更有意义,因此您可以轻松地复制它们并使用它们进行计算或修改值而不用担心副作用。

答案 1 :(得分:55)

类和结构都可以:

  • 定义存储值的属性
  • 定义提供功能的方法
  • 延长
  • 符合协议
  • 定义intialisers
  • 定义下标以提供对其变量的访问

只有班级才能做到:

  • 继承
  • 类型转换
  • 定义deinitialisers
  • 允许多个参考的引用计数。

答案 2 :(得分:29)

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

类是引用类型。这意味着,如果您将一个类的实例分配给一个变量,它只会将引用保存到实例,而不包含副本

答案 3 :(得分:7)

以上答案是正确的我希望我的回答可以帮助那些不理解上述答案的人。

在Swift中有两种类型的对象

  1. 结构
  2. 它们之间的主要区别是

    • 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. 必须声明初始化(constructer)
    2. 有deinitialisers
    3. 可以从其他类继承
    4. <强> STRUCT

      1. 它为您提供免费的初始化程序,如果您执行免费初始化程序,则不必声明初始化程序将被您声明的初始化程序覆盖
      2. 没有deinitialiser
      3. 无法继承其他结构

答案 4 :(得分:6)

这个问题似乎是重复的,但无论如何,以下内容将回答大部分用例:

  1. 结构和类之间最重要的区别之一 是结构是值类型,并始终复制它们 在代码中传递,类是引用类型和 通过引用传递。

  2. 此外,类具有继承,允许一个类继承     另一个人的特征。

  3. 结构属性存储在Stack上,并存储Class实例     因此,有时堆栈比a快得多     类。

  4. Struct自动获取默认初始值设定项,而在Class中,我们     必须初始化。

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

  6. 而且, 总结结构和类之间的区别,有必要理解值和引用类型之间的区别。

    1. 当您复制值类型时,它会复制所有数据 你要复制到新变量中的东西。它们是2个独立的 事物和改变事物不会影响另一方。
    2. 当您复制引用类型时,新变量引用 与您要复制的内容相同的内存位置。这意味着 改变一个将改变另一个,因为他们都指的是 相同的记忆位置。 下面的示例代码可以作为参考。
    3. // 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
      

答案 5 :(得分:3)

如果你在苹果手册中看得更远,你会看到这一部分: “结构和枚举是价值类型”

在本节中,您将看到:

  

“让hd =分辨率(宽度:1920,高度:1080)var   cinema = hd此示例声明一个名为hd的常量并设置它   到使用full的宽度和高度初始化的Resolution实例   高清视频(1920像素宽,1080像素高)。

     

然后声明一个名为cinema的变量并将其设置为当前变量   高清的价值。因为Resolution是一个结构,是现有的副本   实例,并将此新副本分配给电影院。即使   高清和电影现在有相同的宽度和高度,它们是两个   幕后完全不同的实例。

     

接下来,将影院的宽度属性修改为宽度   用于数字电影放映的略宽2K标准(2048   像素宽,1080像素高):

     

cinema.width = 2048检查电影院的宽度属性   它确实变成了2048:

     

println(&#34;影院现在(影院。宽度)像素宽&#34;)//   打印&#34;影院现在是2048像素宽但是,宽度属性   原始的高清实例仍然具有1920年的旧值:

     

println(&#34; hd仍然是(高清宽度)像素宽&#34;)//打印&#34;高清   仍然是1920像素宽“

     

当影院获得当前hd值时,值存储在hd中   被复制到新的电影院实例中。最终结果是两个   完全独立的实例,恰好包含相同的实例   数值。因为它们是单独的实例,所以设置宽度   电影到2048的影响不会影响存储在高清中的宽度。“

     

摘自:Apple Inc.“The Swift Programming Language。”iBooks。   https://itun.es/us/jEUH0.l

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

答案 6 :(得分:1)

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

Swift使用struct来提高性能,即使使用String和Array对象也是如此。

A really good reading here

答案 7 :(得分:1)

这是一个精确显示struct和class之间差异的例子。

在操场上编写代码的屏幕截图
screenshot of written code in playground

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

答案 8 :(得分:1)

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

让我们从一个类开始,这个类符合Equatable只是为了能够比较实例,我们创建一个名为pointClassInstanceA的实例和其他名为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

好的,这里发生了什么,如果我们只更改了pointsClassInstanceB的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建议使用更多的值类型,你可以告诉他们的库是基于结构的,以避免引用类型带来的问题,比如无意中修改了值等。结构是继续使用Swift的方法。 希望它有所帮助。

答案 9 :(得分:1)

Value Type

  • 当您分配或传递 value type时,将创建新的数据副本(实际上使用了写时复制(COW)机制)。
  • 修改实例时,它仅具有本地效果。
  • 使用了堆栈内存

Reference Type

  • 当您分配或传递reference type时,将创建对原始实例的新引用(复制实例的地址)。
  • 当您修改实例时,它具有全局效果,因为该实例可以被指向该实例的任何引用共享和访问。
  • 使用了堆内存

enter image description here

建议

Value type默认使用 Value type的最大优点是通常它们是thread safe

对于Reference type,它们可以被继承,deinit(),可以通过引用===比较实例,Objective-C的互操作性,因为Value Type是在Swift中引入的

More about Mutability

答案 10 :(得分:0)

Alreday有很多关于此的文章,我想在那里加一个类比。希望在此之后你永远不会怀疑: 底线: 类通过引用传递,而结构通过值传递。

假设您正在与朋友分享Google文档资料表。现在,如果他改变了任何内容,您还会看到谷歌文档的更改,意味着您的副本也会受到影响。 这基本上是&#34; 通过引用传递&#34;。

但是假设您的计算机中保存了.XLS文件。您将该文件提供给您的朋友。现在,如果他对该文件进行任何更改,您的文件将不会被搞乱,因为您有自己的副本。 这基本上是&#34; 通过值传递&#34;。 你已经有多个简单的程序在swift playgrounds中检查这个类比。

答案 11 :(得分:0)

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"

答案 12 :(得分:0)

Value Type:复制值类型时(即在将其分配,初始化或传递给函数时),每个实例保留数据的唯一副本。如果您更改一个实例,其他实例也不会更改。

enter image description here

Reference Type:复制引用类型时,每个实例共享数据。引用本身被复制,但不是引用的数据。当您更改一个时,另一个也会更改。

enter image description here

何时应使用结构?

建议默认使用struct。

  • 对简单数据类型使用结构。考虑一下您想在代码中传递的数据库对象,例如NewsItem,Task或User。由于它们定义非常明确,并且通常不需要适应对象之间的复杂关系,因此使用结构更简单。

  • 在多线程环境中(例如,在不同线程中打开了数据库连接),结构更安全。可以将它们从一个线程复制到另一个线程,而不会出现争用情况或死锁的风险。除非故意使类成为线程安全的,否则类没有这种固有的安全性。

  • 当结构的属性也大多是值类型(例如String)时,将它们包装在结构而不是类中是很有意义的。

什么时候应该使用课程?

如果需要班级的特定功能,建议使用班级。

  • 类可以从另一个类继承,而您不能使用结构。
  • 可以取消初始化类。
  • 类具有内置的身份概念,因为它们是引用类型。使用身份运算符===,您可以检查是否有两个引用(指的是同一对象。
  • 如果您需要Objective-C的互操作性,则需要使用类。

可变性

let上使用struct会使该对象成为常数。它不能更改或重新分配,其变量也不能。创建为var的结构可以更改其变量。

由于classes是引用对象,letvar之间的唯一区别是能够将变量重新分配给同一类型的不同类。 letvar关键字不影响更改类中变量的能力。

标记为mutating的函数会更改内部属性值。突变功能仅在structs上存在,并且只能在创建为structs的{​​{1}}上使用。

由于var是值类型,因此它们按值传递。这意味着它们的内容在传递给函数时将被复制。如果需要在函数内部修改struct变量并将其值保留在函数范围之外,则可以将struct变量标记为structs参数。

mutating vs inout

来源是herehere