swift结构体发生深拷贝时,如何将其内部的class类型属性也进行深拷贝

我们知道swift值类型的变量在赋值操作后会放生深拷贝,即:赋值拷贝,而class类型的变量只会浅拷贝。我们看一个例子:

class MyClass {
    var list: [Int]
    
    init(_ list: [Int]) {
        self.list = list
    }
}
struct MyStruct {
    private var cls: MyClass
    var name: String
    
    init(cls: MyClass, name: String) {
        self.cls = cls
        self.name = name
    }
    
    func append(_ el: Int) {
        cls.list.append(el)
    }
    
    func printList() {
        for i in cls.list {
            print("\(name).cls.list: \(i)")
        }
    }
}

var s1 = MyStruct(cls: MyClass([1, 2]), name: "s1")
var s2 = s1
s2.name = "s2" // 此时s2会发生深拷贝,s2成为了一个新的实例
s2.append(3)
s2.append(4)

s1.printList()
print("----------------------")
s2.printList()

打印结果如下:

s1.cls.list: 1
s1.cls.list: 2
s1.cls.list: 3
s1.cls.list: 4
----------------------
s2.cls.list: 1
s2.cls.list: 2
s2.cls.list: 3
s2.cls.list: 4

我们通过打印结果可以看出,s2发生了深拷贝,s2.name被赋予了新的值"s2"。而cls属性是class类型,因此cls属性只是浅拷贝,s1与s2引用的是同一个cls实例。

很明显,我们不希望这样的事情发生,因为s1和s2是两个不同的实例,它们各自应该维护各自的数组列表,而不应该共享同一个数组列表实例。因此我们可以通过如下方式对cls进行深拷贝。

 

struct MyStruct {
    private var cls: MyClass
    var name: String
    
    init(cls: MyClass, name: String) {
        self.cls = cls
        self.name = name
    }
    
    mutating func append(_ el: Int) {
        if !isKnownUniquelyReferenced(&cls) {
            // cls存在多个实例强引用,说明当前MyStruct结构体发生了深度拷贝,而cls是class类型实例,所以cls只会进行浅拷贝,导致存在两个MyStruct结构体实例强引用一个cls实例
            // 因此我们可以在这里将cls进行手动深拷贝
            cls = MyClass(cls.list)
        }
        cls.list.append(el)
    }
    
    func printList() {
        for i in cls.list {
            print("\(name).cls.list: \(i)")
        }
    }
}

var s1 = MyStruct(cls: MyClass([1, 2]), name: "s1")
var s2 = s1
s2.name = "s2" // 此时s2会发生深拷贝,s2成为了一个新的实例
s2.append(3)
s2.append(4)

s1.printList()
print("----------------------")
s2.printList()

打印日志:

s1.cls.list: 1
s1.cls.list: 2
----------------------
s2.cls.list: 1
s2.cls.list: 2
s2.cls.list: 3
s2.cls.list: 4

我们通过输出日志可以看出,cls属性发生了深拷贝,我们通过修改append方法,增加了isKnownUniquelyReferenced判断,判断cls是否存在多个实例强引用,如果存在多个实例引用了cls,那说明s2发生了深拷贝,并对cls进行手动深拷贝。

 

 
posted @ 2021-08-13 18:36  zbblogs  阅读(510)  评论(0编辑  收藏  举报