簡體   English   中英

Swift:如何通過引用而不是值來分配變量?

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

我正在嘗試獲取對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)

當我們查看oddsevens數組時,它們沒有改變,因為我們更改了source數組和destination數組。

我知道我可以在函數上使用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)

有沒有辦法通過引用而不是通過值將這些數組分配給變量?

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)

您不能在Swift中通過引用將數組分配給變量。

“在Swift中,數組,字符串和字典都是值類型...”

資料來源: https : //developer.apple.com/swift/blog/?id=10

如果需要可以通過引用操作的數組,則可以創建一個封裝數組的類並將其用於變量。

這是一個例子:

 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類,並通過引用傳遞。 它做類似的事情,並且在可用方法的常規數組上確實存在差異。

我建議僅出於教學目的使用以下內容,建議不要在生產代碼中使用它。


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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM