【问题标题】:Swift: How to assign a variable by reference, not by value?Swift:如何通过引用而不是值来分配变量?
【发布时间】:2016-07-21 14:33:02
【问题描述】:

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

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)

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

【问题讨论】:

  • stackoverflow.com/questions/24250938/… 在此处查看此答案。你的问题几乎是重复的。基本上,您需要一个函数来使用inout 参数将数组传递给它。请注意回答者所说的,它不是通过引用传递数组,而是作为外部变量的数组的别名。非常酷的解决方法,因为您不能在 swift 中通过引用传递结构,而数组是结构。
  • 在 Swift 中我们有 inout,像: func doSomething(_ value: inout Int32) { value += 123; } 一样使用它(但它不支持默认值,所以只需覆盖它,另一个 func 取少 1 个参数 ;-) ).

标签: swift


【解决方案1】:

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)

【讨论】:

  • 不错的解决方案,但问题是您创建了新数组。我理解最初的问题是用值更新“以前的”数组而不是创建新的数组。除了“inout”之外,还有其他解决方案可以达到这个目的吗?
【解决方案2】:

您不能在 Swift 中通过引用将数组分配给变量。

“在Swift中,Array、String、Dictionary都是值类型……”

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

【讨论】:

    【解决方案3】:

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


    您可以通过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)
    

    【讨论】:

      【解决方案4】:

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

      这是一个例子:

       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 类并通过引用传递。它做了类似的事情,并且确实与可用方法的常规数组存在差异。

      【讨论】:

        猜你喜欢
        • 2013-09-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-06
        • 1970-01-01
        • 1970-01-01
        • 2012-02-05
        • 2015-12-13
        相关资源
        最近更新 更多