复制结构时“幕后”到底发生了什么?

问题描述 投票:0回答:0

在Swift中,

Struct
是值类型,而
Class
是引用类型。因此,两个不同的变量不能指向相同的底层
struct
实例,而它们 can 指向
class
的相同实例。这意味着通过简单地将类实例分配给一个新变量来制作类实例的“浅拷贝”意味着通过第一个变量访问同一实例时会反映通过第二个变量更改实例。这正是您所期望的,并且与我熟悉的其他编程语言(如 JavaScript 中的
Object
)一样工作。

但是,由于

Struct
not 引用类型,您还期望不会有
struct
实例的“浅拷贝”之类的东西 - 每次您将新变量分配给
 struct
,你应该得到一个单独的、具有相同值的新实例。与类的实例不同,更改
struct
的一个属性会丢弃原始的
struct
并用新的替换它,因为
struct
也是不可变的。据推测,这意味着每次复制或更改
struct
时,都会在那时创建一个新实例。创建
struct
的新实例时,我希望运行
init
方法。不过,这实际上并没有发生!

在下面的代码示例中,我使用“浅复制”方法和“深复制”技术复制了同一种

struct
,我将在类的实例上使用这些技术。在编写代码时,我相信两种复制
struct
的方法的结果将完全相同。但是,只有“深拷贝”的方法会导致
struct
init
方法运行,而“浅拷贝”则不会。在两次尝试复制实例后检查
struct
内的静态数组属性的长度可以明显看出这一点。制作“深拷贝”后长度增加,但制作浅拷贝后

对“浅拷贝”进行更改仍然不会导致更改反映在原始包含

struct
的变量中。这 does 反映了基于
struct
s 是不可变值类型我期望发生的事情。

所以,我的问题是:当复制现有结构时(相对于显式调用构造函数时),新结构究竟是如何创建的,为什么它不会导致

init
方法运行?

MCVE 展示了意想不到的结果:

import Foundation

struct TestStruct {
    var name: String
    var value: Int

    init(name: String, value: Int) {
        self.name = name
        self.value = value
        TestStruct.allInstances.append(self)
    }
    
    static var allInstances: [TestStruct] = []
}

import UIKit

class ViewController: UIViewController {
    
    var structs = [TestStruct(name: "First", value: 1), TestStruct(name: "Second", value: 2)]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print("We started with \(TestStruct.allInstances.count) instances in the static array")
        
        var structsCopy: [TestStruct] = []
        for s in structs {
            structsCopy.append(s)
        }
        print("Now there are \(TestStruct.allInstances.count) instances in the static array")
        
        var structsDeepCopy: [TestStruct] = []
        for s in structs {
            structsDeepCopy.append(TestStruct(name: s.name, value: s.value))
        }
        print("Now there are \(TestStruct.allInstances.count) instances in the static array")
        
        print("But, structsCopy isn't a shallow copy, because changing it doesn't change the original array:")
        structsCopy[0].name = "NEW NAME"
        print("structs[0] still has .name \(structs[0].name), while structsCopy[0].name is now \(structsCopy[0].name)")
        
        print("After making changes to a struct in the first copy, there are still just \(TestStruct.allInstances.count) instances in the static array, not 5")
    }
}
swift struct init value-type reference-type
© www.soinside.com 2019 - 2024. All rights reserved.