结构中的快速类在分配期间是不是通过副本传递?

Posted

技术标签:

【中文标题】结构中的快速类在分配期间是不是通过副本传递?【英文标题】:Are swift classes inside struct passed by copy during assignment?结构中的快速类在分配期间是否通过副本传递? 【发布时间】:2017-04-24 01:19:34 【问题描述】:

如果我在 swift 中有一个带有类属性的结构,并且我复制了该结构对象,那么类属性是复制还是通过引用传递?

【问题讨论】:

【参考方案1】:

通过引用传递。你可以测试一下。声明:

class A
struct B  let a = A()

然后:

let b = B()
print("A = \(unsafeAddressOf(b.a))")//0x0000600000019450
let b_copy = b
print("A = \(unsafeAddressOf(b_copy.a))")//0x0000600000019450

【讨论】:

要使其与 swift 5 一起使用:print(Unmanaged.passUnretained(b.a).toOpaque()) 为什么不再调用init?【参考方案2】:

结构体的所有属性在复制结构体时被复制(就像您将旧结构体的每个属性分配 (=) 到新结构体的相应属性一样),无论输入。

当您说“类属性”时,我假设您的意思是引用类型的变量。 (与类同名的类型表示指向该类对象的引用的引用类型。)复制引用类型的值(引用)会产生另一个指向同一对象的引用。请注意,“对象”在 Swift 中不是值——没有“对象类型”——相反,对象总是通过指向它们的引用来操作。

【讨论】:

如果对象是值类型对象(结构)并且其内容中包含少于 3 个单词(ivars),则可以在没有指向它们的引用的情况下表示对象。这些 ivars 的类型必须允许它们在堆栈上分配。在其他情况下,对象将使用相应的指针在堆上分配。来源developer.apple.com/videos/play/wwdc2016/416【参考方案3】:

我在 swift 5 中测试了上述实验: 让我们看看结果:

class A 
    var id: Int
    init(id: Int) 
        self.id = id
    


struct B 
    var grade: Int
    var a: A

根据结果进行实验:

var a = A(id: 1)
var a_copy = a

var b1 = B(grade: 2, a: a)
var copy_b1 = b1

print(b1.a.id)
b1.a.id = 5
print(copy_b1.a.id)

print(b1.grade)
b1.grade = 3
print(copy_b1.grade)

输出:

1
5 // call by reference, same result
2
2 // call by value, no change in result

结论:

struct 在我们创建它的另一个对象时会复制。它复制其结构属性(按值调用)但引用相同的类属性实例(按引用调用)

通过地址做实验:

上做一个实验:

var a = A(id: 1)
var a_copy = a

withUnsafePointer(to: &a)  (address) in
    print("address of a (class) = \(address)")

withUnsafePointer(to: &a_copy)  (address) in
    print("address of a_copy (class) = \(address)")

withUnsafePointer(to: &a.id)  (address) in
    print("address of a.id (struct) = \(address)")

withUnsafePointer(to: &a_copy.id)  (address) in
    print("address of a_copy.id (struct) = \(address)")

输出

address of a (class) = 0x0000000114747f80
address of a_copy (class) = 0x0000000114747f88
address of a.id (struct) = 0x000060000285a390
address of a_copy.id (struct) = 0x000060000285a390

观察 1:

类的两个实例都指向其属性的相同位置。

让我们在 struct 上做实验:

print("\n\n\n")
withUnsafePointer(to: &b1)  (address) in
    print("address of b1 (struct) = \(address)")

withUnsafePointer(to: &b1.grade)  (address) in
    print("address of b1.grade (struct) = \(address)")

withUnsafePointer(to: &b1.a)  (address) in
    print("address of b1.a (class) = \(address)")

withUnsafePointer(to: &b1.a.id)  (address) in
    print("address of b1.a.id (class) = \(address)")

输出

address of b1 (struct) = 0x0000000109382770
address of b1.grade (struct) = 0x0000000109382770
address of b1.a (class) = 0x0000000109382778
address of b1.a.id (class) = 0x0000600001e5cfd0
print("\n\n\n")
withUnsafePointer(to: &copy_b1)  (address) in
    print("address of copy_b1 (struct) = \(address)")

withUnsafePointer(to: &copy_b1.grade)  (address) in
    print("address of copy_b1.grade (struct) = \(address)")

withUnsafePointer(to: &copy_b1.a)  (address) in
    print("address of copy_b1.a (class) = \(address)")

withUnsafePointer(to: &copy_b1.a.id)  (address) in
    print("address of copy_b1.a.id (class) = \(address)")

输出

address of copy_b1 (struct) = 0x0000000109382780
address of copy_b1.grade (struct) = 0x0000000109382780
address of copy_b1.a (class) = 0x0000000109382788
address of copy_b1.a.id (class) = 0x0000600001e5cfd0

结论:&b1.a.id 和 &copy_b1.a.id 都指向同一个地址。

【讨论】:

【参考方案4】:

看来,我们必须考虑修改对象(因为优化器将使用写时复制技术)

值类型(用户)包含引用类型(地址)

观察:

在分配值类型时,任何包含引用类型(例如:地址)的值类型(例如:用户),该引用类型(例如:地址)始终作为引用传递。

引用类型(用户)包含值类型(地址)

观察:

在分配引用类型时,任何引用类型(例如:User)都包含值类型(例如:Address),该值类型(例如:Address)指向同一个父引用对象(例如:User)(父可能包含许多引用 - 例如:u1、u2 都引用相同的内存地址)。

【讨论】:

以上是关于结构中的快速类在分配期间是不是通过副本传递?的主要内容,如果未能解决你的问题,请参考以下文章

为什么可变结构“邪恶”?

通过 Retrofit 中的 Model 类在 GET 请求 URL 中传递 ID

通过副本或参考传递?

Swift 函数是按值还是按引用分配/传递?

通过作为方法参数传递对象的副本

我可以使用副本集名称通过 mongo-connector 进行连接吗