简体   繁体   English

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

[英]Swift: How to assign a variable by reference, not by value?

I'm trying to get a reference to an Array and make modifications to it. 我正在尝试获取对Array的引用并对其进行修改。 Because Array s in Swift are value types, instead of reference types, if I assign my array to a variable first, I am getting a copy of the array instead of the actual 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)

When we look at the odds and evens arrays, they are unaltered because we changed the source and destination arrays. 当我们查看oddsevens数组时,它们没有改变,因为我们更改了source数组和destination数组。

I know that I can use the inout parameter attribute on a function to pass them by reference, instead of by value: 我知道我可以在函数上使用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)

Is there a way to assign these arrays to a variable by reference, instead of by value? 有没有办法通过引用而不是通过值将这些数组分配给变量?

Array is a struct, which means it's a value type in Swift. Array是一个结构,这意味着它是Swift中的值类型。 Because of this, arrays always behave according to value and not reference semantics. 因此,数组始终根据值而不是引用语义来运行。 The problem here is that you're attempting to use mutable, reference based logic to operate on values types. 这里的问题是,您正在尝试使用可变的,基于引用的逻辑对值类型进行操作。

You don't want to rely on mutations occurring inside the function to propagate back to the caller. 您不想依赖于函数内部发生的变异来传播回调用者。 As you've found, this is only possible with inout parameters. 如您所见,这只能通过inout参数来实现。 What you should do instead is return the mutated array from the function back to the caller. 相反,您应该做的是将变异数组从函数返回给调用者。 The point of value oriented programming is that it shouldn't matter which array you have, but rather that any two equivalent arrays or values types are interchangeable. 面向值编程的重点在于,您拥有哪个数组无关紧要,而是任何两个等效的数组或值类型都是可互换的。

It's slightly easier to imagine with another value type. 使用其他值类型更容易想象。 Take an Int for example, and this function that does some math. 以一个Int为例,该函数可以进行一些数学运算。

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

Now consider a similar function, but written in the reference oriented style that you're attempting to use: 现在考虑一个类似的函数,但是以您尝试使用的面向参考的样式编写:

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

You can see it's simply not natural to operate on value types this way. 您可以看到以这种方式对值类型进行操作根本不自然。 Instead just return the updated value (the modified arrays) from your function and carry on from there. 相反,只需从函数中返回更新后的值(修改后的数组)并从那里进行即可。

Here is your function refactored with value semantics. 这是使用值语义重构的函数。

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)

You cannot assign an array to a variable by reference in Swift. 您不能在Swift中通过引用将数组分配给变量。

"In Swift, Array, String, and Dictionary are all value types..." “在Swift中,数组,字符串和字典都是值类型...”

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

If you need arrays that can be manipulated by reference you can create a class that encapsulates an array and use it for your variables. 如果需要可以通过引用操作的数组,则可以创建一个封装数组的类并将其用于变量。

here's an example: 这是一个例子:

 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"]

Note that you could also define your arrays as NSMutableArrays which are Obj-C classes and are passed by reference. 请注意,您还可以将数组定义为NSMutableArrays,它们是Obj-C类,并通过引用传递。 It does a similar thing and does present differences with a regular array for the methods that are available. 它做类似的事情,并且在可用方法的常规数组上确实存在差异。

I recommend this the following only for didactic purpose only, I advise against using it in production code. 我建议仅出于教学目的使用以下内容,建议不要在生产代码中使用它。


You can circulate a "reference" to something via an UnsafePointer instance. 您可以通过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"]

Thus, you can simulate a reference to a variable via the above class, which stores a pointer to the given variable reference. 因此,您可以通过上述类模拟对变量的引用,该类存储指向给定变量引用的指针。

Please note that this is a simple use case, and will behave as expected only if if the variable doesn't get destroyed before the pointer , as in that case the memory initially occupied by the variable will be replaced by something else, and the unsafe pointer will no longer be valid. 请注意,这是一个简单的用例,并且只有在变量没有在指针之前销毁的情况下,它才会表现出预期的效果 ,因为在这种情况下,变量最初占用的内存将被其他东西替换,并且不安全指针将不再有效。 Take for example the next code: 以下面的代码为例:

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM