Swift对象引用到数组?

6

我可能错过了关于Swift的重要信息。我有一个包含键/值为Swift数组的映射。我更改了数组,但映射内部的数组未发生变化。能否有人解释一下发生了什么?谢谢!

var map = [String:[String]]()
var list = [String]()
map["list"] = list //output: "["list": []]\n"
print(map)

list.append("test")
print(list)  //output: "["test"]\n"
print(map)   //output: "["list": []]\n"

4
Swift数组是按值传递的,也就是说会被复制。你必须将其设置回映射中,或者使用某种引用包装器:https://dev59.com/yGAf5IYBdhLWcg3w0VUf - Thilo
我使用了包装类来实现这个功能。不过我想知道是否有一种方法可以使用指向数组的指针,然后在其他地方从指针创建一个数组。 - Fred Faust
在Swift中,无法像使用指针一样使用数组,因为其工作方式不同。 - Stefan Salatic
在Swift中,没有办法为数组使用指针这一说法并不完全正确。 - matt
4个回答

6

正如您所知,这些(字典和数组)是值类型。因此,通常你要做的技巧就是从字典中取出数组,修改它,再放回去:

var map = [String:[String]]()
map["list"] = [String]()

var list = map["list"]!
list.append("test")
map["list"] = list

然而,还有另一种方法:你可以得到一个指向字典内部数组的指针,但只有在使用带有inout参数的函数时才能这样做。例如:

var map = [String:[String]]()
map["list"] = [String]()

func append(s : String, inout to arr : [String]) {
    arr += [s]
}
append("test", to: &(map["list"]!))

实际上,这只是同一件事的一种表示法。但如果你需要经常做这样的事情,你可能更喜欢使用它。


请参阅我书中的此部分:http://www.apeth.com/swiftBook/ch04.html#SECreferenceTypes - matt

2

假设我们在Swift中有一个类型,它是一个class

class MyClass {
    var a:Int
    init(i:Int) {
        a = i
    }
}

let aClass = MyClass(i: 10)
var bClass = aClass
bClass.a = 20
aClass.a // 20
aClass.a = 30
bClass.a // 30

这里展示了指针样式的行为:aClass和bClass指向同一个对象。如果你在其中一个对象中改变属性值,两个对象都会改变,因为它们是相同的。

结构体

当使用struct时,行为会发生改变,如下所示:

struct MyStruct {
    var a:Int
    init(i:Int) {
        a = i
    }
}
let aStruct = MyStruct(i: 10)
var bStruct = aStruct
bStruct.a = 20
aStruct.a // 10

使用struct和enum代替单个实例和多个指向该实例的指针,当我们将值分配给新实例时会创建一个新实例。它是原始实例的副本,并且它们彼此独立。
由于Swift数组类型是struct,因此它具有复制而不是指向的行为。而NSArray和NSMutableArray仍然是类(如ObjC中),因此具有类的指向行为。
可以使用inout属性和计算变量来部分复制指针的行为,以使struct表现得像class。
struct MyStruct {
    lazy var map = [String:[String]]()
    var list:[String] {
        mutating get {
            if map["list"] == nil {
                map["list"] = [String]()
            }
            return map["list"]!
        }
        set {
            map["list"] = newValue
        }
        
    }
}

var mS = MyStruct()
mS.list = ["test"]
mS.map // ["list": ["test"]]
mS.list // ["test"]

在类型实例之外,您可以执行以下操作:

var map = [String:[String]]()
var list = [String]()
var appendToList = { (a:String) in
    list.append(a)
    map["list"] = list
}
appendToList("test") // list and map["list"] are both updated

1
Swift中的数组被定义为结构体,即值类型。当您将另一个变量分配给值类型的另一个变量时,它会创建该第二个变量的副本:
map["list"] = list   // store a **copy** of list into map

为了看到值类型和引用类型之间的区别,请将您的数组更改为其ObjectiveC表亲NSMutableArray,它是引用类型:
var map = [String: NSMutableArray]()
var list = NSMutableArray()
map["list"] = list

list.addObject("test")

print(map)   // ["list": (test)]

0
正如Code Different所说,Swift中的Arrays是结构体,因此您正在存储数组的副本。
如果您真的想继续使用Swift数组,我建议先填充数组“list”,然后再复制到“map”。这应该适合您。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接