[英]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)
當我們查看odds
和evens
數組時,它們沒有改變,因為我們更改了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中,數組,字符串和字典都是值類型...”
如果需要可以通過引用操作的數組,則可以創建一個封裝數組的類並將其用於變量。
這是一個例子:
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.