English 中文(简体)
快速形成新的自我影印机是否具有突变结构的功能?
原标题:Does a mutating struct function in swift create a new copy of self?

我很喜欢迅速的价值观调子,但我担心变体功能的履行。 附录

struct Point {
   var x = 0.0
   mutating func add(_ t:Double){
      x += t
   }
}

如今,我们建立了<条码><><>>>>>,并做了改动:

var p = Point()
p.add(1)

现在,记忆中现有的<条码>已变,或<条码>自封/条码>改为新例,如上所示。

self = Point(x:self.x+1)
最佳回答

Now does the existing struct in memory get mutated, or is self replaced with a new instance

Conceptually, these two options are exactly the same. I ll use this example struct, which uses UInt8 instead of Double (because its bits are easier to visualize).

struct Point {
    var x: UInt8
    var y: UInt8

    mutating func add(x: UInt8){
       self.x += x
    }
}

我提出了这一结构的新例子:

var p = Point(x: 1, y: 2)

静态地把一些记忆放在 st上。 它看着这样的情况:

00000000  00000001  00000010  00000000
<------^  ^------^  ^------^ ^----->
other     |self.x | self.y | other memory
          ^----------------^
          the p struct

让我们看看在这两种情况下会发生什么情况,我们称之为<编码>p.add(x:3):

  1. 现有结构在内部变换:

    我们的记忆将照此办理:

     00000000  00000100  00000010  00000000
     <------^  ^------^  ^------^ ^----->
     other    | self.x | self.y | other memory
             ^----------------^
             the p struct
    
  2. 自我取而代之的是新情况:

    我们的记忆将照此办理:

     00000000  00000100  00000010  00000000
     <------^  ^------^  ^------^ ^----->
     other    | self.x | self.y | other memory
             ^----------------^
             the p struct
    

通知说两种情况之间没有差别。 这是因为将新价值分配给内部的自行成因。 <代码>p总是在记分栏上用同样的两个字标。 将自己的新价值转让给<代码>p<>>>/code>将只会以斜体取代这2条内容,但仅用同一两条 by。

现在,can是两种假设情景之间的一种区别,涉及初始使用人的任何可能的副作用。 证明这一点是我们的方向,而是:

struct Point {
    var x: UInt8
    var y: UInt8
    
    init(x: UInt8, y: UInt8) {
        self.x = x
        self.y = y
        print("Init was run!")
    }

    mutating func add(x: UInt8){
       self.x += x
    }
}

When you run var p = Point(x: 1, y: 2), you ll see that Init was run! is printed (as expected). But when you run p.add(x: 3), you ll see that nothing further is printed. This tells us that the initializer is not anew.

问题回答

I feel it s worth taking a look (from a reasonably high-level) at what the compiler does here. If we take a look at the canonical SIL emitted for:

struct Point {
    var x = 0.0
    mutating func add(_ t: Double){
        x += t
    }
}

var p = Point()
p.add(1)

We can see that the add(_:) method gets emitted as:

// Point.add(Double) -> ()
sil hidden @main.Point.add (Swift.Double) -> () :
           $@convention(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 Immediatec -emit-sil main.swift ×crun Immediate-demangle > main.silgen

The important thing here is how Swift treats the implicit self parameter. You can see that it s been emitted as an @inout parameter, meaning that it ll be passed by reference into the function.

In order to perform the mutation of the x property, the struct_element_addr SIL instruction is used in order to lookup its address, and then the underlying _value property of the Double. The resultant double is then simply stored back at that address with the store instruction.

这意味着,<代码>add(_:>”方法能够直接改变记忆中<代码>p<>/code>x 的财产的价值,而不造成任何中间的<代码><><>>>>>>>>>。

我这样做了:

import Foundation

struct Point {
  var x = 0.0
  mutating func add(_ t:Double){
    x += t
  }
}

var p = Point()

withUnsafePointer(to: &p) {
  print("(p) has address: ($0)")
}

p.add(1)

withUnsafePointer(to: &p) {
  print("(p) has address: ($0)")
}

产出:

点(x:0.0)有地址: 0x000000010fc2fb80

Point(x:1.0)有地址: 0x000000010fc2fb80

鉴于记忆地址没有改变,我必须改变方向,而不是取而代之。

为了完全取而代之,你必须使用另一个记忆地址,因此在原始记忆地址复制该物体是毫无意义的。

或许通过这种思路更容易理解,即使结构已变,因为其价值类型不同,新价值与旧价值不同,因此,根据价值类型的定义,它们是不同的。





相关问题
Nested struct variable initialization

How can I initialize this nested struct in C? typedef struct _s0 { int size; double * elems; }StructInner ; typedef struct _s1 { StructInner a, b, c, d, e; long f; char[16] s; }...

passing primitive or struct type as function argument

I m trying to write some reasonably generic networking code. I have several kinds of packets, each represented by a different struct. The function where all my sending occurs looks like: - (void)...

Error: "Cannot modify the return value" c#

I m using auto-implemented properties. I guess the fastest way to fix following is to declare my own backing variable? public Point Origin { get; set; } Origin.X = 10; // fails with CS1612 Error ...

Sending struct over TCP (C-programming)

I have a client and server program where I want to send an entire struct from the client and then output the struct member "ID" on the server. I have done all the connecting etc and already managed ...

Scheme, getting the pointer from pointed struct

Assume I have a such struct: (define-struct node (value next)) ;and making 2 nodes, parent pointing to child as next. (define child (make-node 2 null)) (define parent (make-node 1 child)) Under ...

Changing a struct inside another struct in a foreach loop

The following code prints (when invoking MyMethod): 0 0 0 1 I would expect it to print: 0 0 1 1 Why is this? Code: private struct MyStruct { public MyInnerStruct innerStruct; } private ...

热门标签