我覺得值得一看(從合理的高水平)編譯器在這裏做什麼。如果我們看一看規範SIL用於發射:
struct Point {
var x = 0.0
mutating func add(_ t: Double){
x += t
}
}
var p = Point()
p.add(1)
我們可以看到,add(_:)
方法時發出爲:
// Point.add(Double) ->()
sil hidden @main.Point.add (Swift.Double) ->() :
[email protected](method) (Double, @inout Point) ->() {
// %0 // users: %7, %2
// %1 // users: %4, %3
bb0(%0 : $Double, %1 : $*Point):
// get address of the property 'x' within the point instance.
%4 = struct_element_addr %1 : $*Point, #Point.x, loc "main.swift":14:9, scope 5 // user: %5
// get address of the internal property '_value' within the Double instance.
%5 = struct_element_addr %4 : $*Double, #Double._value, loc "main.swift":14:11, scope 5 // users: %9, %6
// load the _value from the property address.
%6 = load %5 : $*Builtin.FPIEEE64, loc "main.swift":14:11, scope 5 // user: %8
// get the _value from the double passed into the method.
%7 = struct_extract %0 : $Double, #Double._value, loc "main.swift":14:11, scope 5 // user: %8
// apply a builtin floating point addition operation (this will be replaced by an 'fadd' instruction in IR gen).
%8 = builtin "fadd_FPIEEE64"(%6 : $Builtin.FPIEEE64, %7 : $Builtin.FPIEEE64) : $Builtin.FPIEEE64, loc "main.swift":14:11, scope 5 // user: %9
// store the result to the address of the _value property of 'x'.
store %8 to %5 : $*Builtin.FPIEEE64, loc "main.swift":14:11, scope 5 // id: %9
%10 = tuple(), loc "main.swift":14:11, scope 5
%11 = tuple(), loc "main.swift":15:5, scope 5 // user: %12
return %11 : $(), loc "main.swift":15:5, scope 5 // id: %12
} // end sil function 'main.Point.add (Swift.Double) ->()'
(通過運行xcrun swiftc -emit-sil main.swift | xcrun swift-demangle > main.silgen
)
重要這裏的事情是Swift如何處理隱含的self
參數。您可以看到它已經作爲@inout
參數發出,這意味着它將通過參考傳遞到函數中。
爲了執行x
屬性的突變,所述struct_element_addr
SIL指令,以便用於查找它的地址,然後將Double
的底層_value
屬性。然後通過store
指令簡單地將結果的雙精度值存儲回該地址。
這意味着,該方法add(_:)
能夠直接改變存儲器p
的x
屬性的值,而無需創建的Point
任何中間實例。
對於值類型,「內存中的現有結構發生變異」和「自身被新實例替換」意味着同樣的事情。該結構存在於堆棧中的一些字節數。無論你改變結構的單個字段,還是分配一個新的結構,結構仍然存在於這些相同的字節中。 – Alexander