Swift: how to assign a variable by reference, not by value?

I am trying to get a link to Array and make changes to it. Since Swift's Array are value types, not reference types, if I assign my array to a variable first, I get a copy of the array instead of the actual 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 odds and evens , they do not change, because we changed the source and destination arrays.

I know that I can use the inout parameter attribute for a function that passes them by reference, and not by value:

 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 value?

+10
source share
4 answers

Array is a structure that means the type of value in Swift. Because of this, arrays always behave according to value, rather than referential semantics. The problem here is that you are trying to use mutable link-based logic to work with value types.

You do not want to rely on mutations that occur inside a function to propagate back to the caller. As you have already seen, this is only possible with inout parameters. Instead, you should return the mutated array from the function back to the caller. The point of value-oriented programming is that it doesn't matter which array you have, but rather that any two equivalent arrays or value types are interchangeable.

A bit easier to imagine with a different type of value. Take Int, for example, and this function, which does some math.

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

Now consider a similar function, but written in the style of orienteering that you are trying to use:

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

You can see that it is simply unnatural to work with value types in this way. Instead, just return the updated value (modified arrays) from your function and continue from there.

Here is your function reorganized using 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) 
+4
source

You cannot assign an array to a variable by reference in Swift.

"In Swift, Array, String, and Dictionary, all value types ..."

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

+1
source

If you need arrays that can be manipulated by reference, you can create a class that encapsulates the array and use it for your variables.

here is 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 can also define your arrays as NSMutableArrays, which are Obj-C classes and are passed by reference. It does a similar thing and represents the differences with a regular array for available methods.

+1
source

I recommend the following for didactic purposes only, I do not recommend using it in working code.


You can distribute a “link” to something through an UnsafePointer instance.

 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 link to a variable using the above class, which stores a pointer to this link to the variable.

Please note that this is a simple use case, and it will behave as expected only if the variable is not destroyed before the pointer , since in this case the memory originally occupied by the variable will be replaced with something else, and the unsafe pointer will no longer be valid. Take for example the following 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 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) 
0
source

All Articles