Will Swift copy the mutation in this scenario?

Essentially, I want a temporary alias for a class property to improve readability.

I am in the situation described in the following code, and I do not see a direct solution. What I want to avoid, y copied to the mutation, and then copied back. Renaming y greatly facilitate the readability of the real algorithm.

Is the Swift compiler smart enough not to actually allocate new memory, and how could I know?

If not, how to prevent copying?

 class myClass { var propertyWithLongDescriptiveName: [Float] func foo() { var y = propertyWithLongDescriptiveName // mutate y with formulas where y corresponds to a `y` from some paper // ... propertyWithLongDescriptiveName = y } // ... } 
+6
source share
2 answers

struct Array are value types in Swift, which means that they are always copied when assigning to another variable. However, each struct Array contains pointers (not displayed in the public interface) for storing elements. Therefore after

 var a = [1, 2, 3, 4] var b = a 

both a and b are (formally independent) values, but with pointers to the same element store. Only when one of them is mutated is a copy of the element store created. This is called β€œcopy on write” and, for example, is explained in

So after

 b[0] = 17 

a and b are values ​​with pointers to different (independent) repositories of elements. A further mutation of b does not copy the element store again (unless b is copied to another variable). Finally, if you assigned the value back

 a = b 

the old repository of elements a freed, and both values ​​are again pointers to the same repository.

Therefore, in your example:

  var y = propertyWithLongDescriptiveName // ... mutate y ... propertyWithLongDescriptiveName = y 

a copy of the element store is made exactly once (provided that you do not copy y into an additional variable).

If the size of the array does not change, then a possible approach might be

 var propertyWithLongDescriptiveName = [1.0, 2.0, 3.0, 4.0] propertyWithLongDescriptiveName.withUnsafeMutableBufferPointer { y in // ... mutate y ... y[0] = 13 } print(propertyWithLongDescriptiveName) // [13.0, 2.0, 3.0, 4.0] 

withUnsafeMutableBufferPointer() causes a closure using UnsafeMutableBufferPointer in the element store. A UnsafeMutableBufferPointer is a RandomAccessCollection and therefore offers a massive interface.

+4
source

No, the Swift compiler is not so smart. All you need is a little test to see what it does:

 class MyClass { var propertyWithLongDescriptiveName: [Float] = [1,2] func foo() { var y = propertyWithLongDescriptiveName y[0] = 3 // copied an mutated print(y) // [3,2] print(propertyWithLongDescriptiveName) // [1,2] } } let mc = MyClass() mc.foo() 

You have 2 options:

  • Change propertyWithLongDescriptiveName to NSMutableArray , which is a reference type
  • Accept the overhead of copying and mutation to trade for the convenience of reading your algorithm. In many cases, the cost of memory allocation is minimal compared to your algorithm.
+2
source

Source: https://habr.com/ru/post/1013305/


All Articles