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

前端 未结 4 1747
青春惊慌失措
青春惊慌失措 2020-12-19 02:08

I\'m trying to get a reference to an Array and make modifications to it. Because Arrays in Swift are value types, instead of reference types, if I

相关标签:
4条回答
  • 2020-12-19 02:28

    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.

    0 讨论(0)
  • 2020-12-19 02:42

    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)
    
    0 讨论(0)
  • 2020-12-19 02:42

    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

    0 讨论(0)
  • 2020-12-19 02:48

    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)
    
    0 讨论(0)
提交回复
热议问题