【问题标题】:structure vs class in swift languageswift语言中的结构与类
【发布时间】:2014-08-04 17:41:36
【问题描述】:

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

谁能帮我理解这意味着什么?对我来说,类和结构似乎是一样的。

【问题讨论】:

  • 查看 .NET 中结构和类的区别:stackoverflow.com/a/13275/19100,我猜 Swift 使用相同的语义。
  • @jonrsharpe 对您来说可能很容易吗?如果你知道这个,你能给我答案吗
  • 值与引用不是 OOP 唯一的概念。它在 C 语言中,例如 void my_func(int a)void my_func(int &a)。这是编程的一个非常基本的问题。阅读更多:stackoverflow.com/questions/373419/…

标签: swift


【解决方案1】:

这是一个带有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 更有意义,这样您就可以轻松地复制它们并使用它们进行计算或修改值,而不必担心副作用。

【讨论】:

  • "但是对于那些 no 比简单的数字更复杂的值..." 感谢 Alex
  • @MichaelRapadas 数字实际上 Swift 中的结构。
  • 你能澄清一下aStruct and bStruct are two structs with the same value!这让我很困惑,因为结构内的变量值是不同的。
  • @JulianKról A 那行 aStructbStruct 具有相同的值。它们都有一个name 字段,设置为"Bob"。但它们是两种不同的结构。当您可以更改其中一个结构的名称而另一个保持不变时,这一点在下一行得到证明。
  • 刚刚错过了作业。很清楚,谢谢。也许外面太热了:-)
【解决方案2】:

类和结构都可以:

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

只有班级才能做到:

  • 继承
  • 类型转换
  • 定义去初始化器
  • 允许对多个引用进行引用计数。

【讨论】:

    【解决方案3】:

    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
    

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

    【讨论】:

    • +1 表示“如果将类的实例分配给另一个变量,它将只保存实例的引用而不是复制。”
    【解决方案4】:

    以上答案是正确的,希望我的回答对不理解以上答案的人有所帮助。

    在 Swift 中有两种类型的对象

    1. 结构体

    它们之间的主要区别是

    • 结构是类型
    • 类是reference类型

    例如这里的代码就很好理解了。

    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. 具有取消初始化程序
    3. 可以从其他类继承

    结构

    1. 它有免费的初始化器,你不必声明初始化器,如果你做免费的初始化器将被你声明的初始化器覆盖
    2. 没有取消初始化程序
    3. 不能从其他结构继承

    【讨论】:

      【解决方案5】:

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

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

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

      3. Struct 属性存储在 Stack 中,Class 实例存储在 因此,在堆上,有时堆栈比 类。

      4. Struct 自动获取默认初始化程序,而在 Class 中,我们 必须初始化。

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

      还有, 总结一下结构体和类的区别,有必要了解一下值类型和引用类型的区别。

      1. 当您复制一个值类型时,它会从 您要复制到新变量中的内容。它们是 2 独立的 改变一件事情不会影响另一件事情。
      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
      

      【讨论】:

      • 嗨 Dilip,你能举个例子说明“结构在任何时候都是线程安全的或单例的。”?提前致谢。
      • 结构在任何时候都不是线程安全的或单例的。
      【解决方案6】:

      Swift 类型

      Value type 是一种类型,其值在分配给变量或常量、传递给函数或从函数返回时被复制。 (还有asis 检查复制结构)

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

      值类型

      Struct, Enum[About], Tuple
      struct String, struct Array(Set, Dictionary)

      (Objective-C int...)

      字符串和内置集合值类型包含对heap内部引用以管理其大小

      • 当您分配或传递 value type 时,会创建一个新的数据副本copy on write - COW 机制用于 一些 特定类(如 Collections(Array, Dictionary, Set))[About] 并进行了一些优化,例如在修改对象时创建副本.对于自定义类型,您应该自己支持 COW
      • 当您修改一个实例时,它只具有本地效果。
      • 通常使用堆栈内存[About]

      参考类型
      Class, Function

      (Objective-C 所有其他)

      使用ARC

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

      Value type 建议默认使用Value type最大的优势就是通常都是thread safe

      Reference type 优点:

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

      [Stack vs Heap]
      [let vs var, class vs struct]
      [Class vs Structure]

      Choosing Between Structures and Classes
      Types
      Classes And Structures

      【讨论】:

        【解决方案7】:

        如果您在苹果手册中进一步查看,您将看到以下部分: “结构和枚举是值类型”

        在本节中,您将看到:

        “​让​​​hd​=​分辨率​(​宽度​:​1920​,​高度​:​1080​)​var​ ​cinema​ = ​hd 这个例子声明了一个叫做 hd 的常量并设置它 到一个使用完整宽度和高度初始化的分辨率实例 高清视频(1920 像素宽 x 1080 像素高)。

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

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

        ​cinema​.​width​ = ​2048 检查电影放映的宽度属性 它确实已更改为 2048:

        ​println​(​"电影现在是​(​cinema​.​width​)​像素宽"​)​// 打印“电影现在是 2048 像素宽但是,宽度属性 原始的 hd 实例仍然具有 1920 的旧值:

        println​(​"hd 仍然是​(​hd​.​width​)​ 像素宽"​) // 打印“hd 仍然是 1920 像素宽”

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

        摘自:Apple Inc. “Swift 编程语言”。电子书。 https://itun.es/us/jEUH0.l

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

        【讨论】:

          【解决方案8】:

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

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

          A really good reading here

          【讨论】:

            【解决方案9】:

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

            让我们从一个类开始,这个类符合 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 的方式。 希望对您有所帮助。

            【讨论】:

              【解决方案10】:

              这是一个例子,准确地展示了结构和类之间的区别。

              操场上编写代码的屏幕截图

              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
              

              【讨论】:

                【解决方案11】:
                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】:

                  已经有很多关于此的文章,我想在此处添加一个类比。希望您在此之后永远不会有任何疑问: 底线: 类通过引用传递,而结构体通过值传递。

                  假设您正在与您的朋友共享一个 google 文档表。现在,如果他更改了其中的任何内容,您还将在您的 google 文档上看到更改,这意味着您的副本也会受到影响。 这基本上是“通过引用传递”。

                  但是假设,如果您的机器中保存了一个 .XLS 文件。您将该文件提供给您的朋友。现在,如果他对该文件进行任何更改,您的文件将不会被弄乱/影响,因为您有自己的副本。 这基本上是“按值传递”。 您已经有多个简单的程序可以在 Swift Playground 中检查这个类比。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2014-07-29
                    • 2019-08-28
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-11-20
                    相关资源
                    最近更新 更多