I\'m trying to get a reference to an Array
and make modifications to it. Because Array
s in Swift are value types, instead of reference types, if I
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. It does a similar thing and does present differences with a regular array for the methods that are available.
Array
is a struct, which means it's a value type in 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. 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.
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.
"In Swift, Array, String, and Dictionary are all value types..."
Source: https://developer.apple.com/swift/blog/?id=10
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.
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)