Swift:如何通过引用而不是按值来分配变量?

时间:2016-07-21 14:33:02

标签: swift

我正在尝试获取对Array的引用并对其进行修改。因为Swift中的Array是值类型而不是引用类型,如果我首先将我的数组赋值给变量,我得到的是数组的副本而不是实际的数组:

var odds = ["1", "3", "5"]
var evens = ["2", "4", "6"]

var source = odds
var destination = evens

var one = odds.first!

source.removeFirst() // only removes the first element of the `source` array, not the `odds` array

destination.append(one)

当我们查看oddsevens数组时,它们不会改变,因为我们更改了sourcedestination数组。

我知道我可以在函数上使用inout参数属性来通过引用而不是通过值传递它们:

func move(inout source: [String], inout destination: [String], value:String) {
    source.removeAtIndex(source.indexOf(value)!)
    destination.append(value)
}

move(&odds, destination: &evens, value:one)

有没有办法通过引用而不是按值将这些数组分配给变量?

4 个答案:

答案 0 :(得分:4)

Array是一个结构,这意味着它是Swift中的值类型。因此,数组总是根据值而不是引用语义来表现。这里的问题是你试图使用可变的,基于引用的逻辑来操作值类型。

您不希望依赖函数内部发生的突变传播回调用者。正如您所发现的,这只能通过inout参数实现。你应该做的是将变异的数组从函数返回给调用者。价值导向编程的重点在于你拥有哪个数组并不重要,而是任何两个等价的数组或值类型都可以互换。

使用其他值类型可以更容易想象。以Int为例,这个函数做了一些数学运算。

func addFive(int: Int) -> Int {
    return int + 5
}

现在考虑一个类似的功能,但是用你试图使用的参考导向样式编写:

func addFive(inout int: Int) {
    int = int + 5
}

您可以看到以这种方式操作值类型是不自然的。而只是从您的函数返回更新的值(修改后的数组)并从那里继续。

这是用值语义重构的函数。

func move(source: [String], destination: [String], value:String) -> ([String], [String]) {

    var mutableSource = source
    var mutableDestination = destination

    mutableSource.removeAtIndex(source.indexOf(value)!)
    mutableDestination.append(value)

    return (mutableSource, mutableDestination)
}

let (updatedSource, updatedDestination) = move(odds, destination: evens, value:one)

答案 1 :(得分:1)

您无法通过Swift中的引用将数组赋值给变量。

“在Swift中,数组,字符串和字典都是值类型......”

来源:https://developer.apple.com/swift/blog/?id=10

答案 2 :(得分:1)

如果需要可以通过引用操作的数组,可以创建一个封装数组并将其用于变量的类。

这是一个例子:

 class ArrayRef<Element>:CustomStringConvertible
 {
    var array:[Element]=[]

    init()               {}
    init(Type:Element.Type)    {}
    init(fromArray:[Element])  { array = fromArray }
    init(_ values:Element ...) { array = values }

    var count:Int { return array.count }

    // allow use of subscripts to manipulate elements
    subscript (index:Int) -> Element
    {
       get { return array[index] }
       set { array[index] = newValue }
    }

    // allow short syntax to access array content
    // example:   myArrayRef[].map({ $0 + "*" })  
    subscript () -> [Element]
    {
       get { return array }
       set { array = newValue }
    }

    // allow printing the array example:  print(myArrayRef) 
    var description:String { return "\(array)" }

    // delegate append method to internal array
    func append(newElement: Element)
    { array.append(newElement) }

    // add more delegation to array methods as needed ...
 }

 // being an object, the ArrayRef class is always passed as a reference
 func modifyArray(X:ArrayRef<String>)
 {
    X[2] = "Modified"
 }
 var a = ArrayRef("A","B","C")
 modifyArray(a)
 print(a) // --> a is now ["A", "B", "Modified"]


 // various means of declaration ...
 var b = ArrayRef<String>()
 b[] = ["1","2","3"]

 var c = ArrayRef(fromArray:b[])

 // use .array to modify array content (or implement delegation in the class)
 c.array += a[] + ["X","Y","Z"]

请注意,您还可以将数组定义为NSMutableArrays,它们是Obj-C类并通过引用传递。它做了类似的事情,并且为可用的方法提供了与常规数组的差异。

答案 3 :(得分:0)

我建议仅出于教学目的使用以下内容,建议不要在生产代码中使用它。


您可以通过UnsafePointer实例传播对某物的“引用”。

class Ref<T> {
    private var ptr: UnsafePointer<T>!
    var value: T { return ptr.pointee }

    init(_ value: inout T) {
        withUnsafePointer(to: &value) { ptr = $0 }
    }
}

var a = ["1"]
var ref = Ref(&a)
print(a, ref.value) // ["1"] ["1"]
a.append("2")
print(a, ref.value) // ["1", "2"] ["1", "2"]
ref.value.removeFirst()
print(a, ref.value) // ["2"] ["2"]

因此,您可以通过上述类模拟对变量的引用,该类存储指向给定变量引用的指针。

请注意,这是一个简单的用例,并且仅在变量没有在指针之前销毁的情况下才会按照预期的方式运行,因为在这种情况下,变量最初占用的内存将被其他替换,不安全的指针将不再有效。以下面的代码为例:

var ref: Ref<[String]>!
// adding an inner scope to simulate `a` being destroyed
do {
    var a: [String] = ["a"]
    ref = Ref(&a)
    print(a, ref.value)
    a = ["b"]
    print(a, ref.value)
}
// `a` was destroyed, however it's place on the stack was taken by `b`
var b: [String:Int] = ["a": 1]
// however `b` is not an array, thus the next line will crash
print(ref.value)