Array
реализован с поведением копирования при записи — вы получите его независимо от каких-либо оптимизаций компилятора (хотя, конечно, оптимизации могут уменьшить количество случаев, когда необходимо копирование).
На базовом уровне Array
— это просто структура, которая содержит ссылку на выделенный в куче буфер, содержащий элементы, поэтому несколько экземпляров Array
могут ссылаться на один и тот же буфер. Когда вы приступите к изменению данного экземпляра массива, реализация проверит, является ли буфер уникальным, и если это так, измените его напрямую. В противном случае массив выполнит копию базового буфера, чтобы сохранить семантику значений.
Однако с вашей структурой Point
вы не реализуете копирование при записи на уровне языка. Конечно, как говорит @Alexander, это не не мешать компилятору выполнять все виды оптимизации, чтобы минимизировать стоимость копирования целых структур. Тем не менее, эти оптимизации не должны точно следовать поведению копирования при записи — компилятор просто может делать все, если программа работает в соответствии со спецификацией языка.
В вашем конкретном примере и p1
, и p2
являются глобальными, поэтому компилятору необходимо сделать их отдельными экземплярами, поскольку другие файлы .swift в том же модуле имеют к ним доступ (хотя это потенциально может быть оптимизировано за счет оптимизации всего модуля). Однако компилятору по-прежнему не нужно копировать экземпляры — он может просто ">оценить добавление с плавающей запятой во время компиляции и инициализировать один из глобальных переменных с помощью 0.0
, а другой с 1.0
.
А если бы они были локальными переменными в функции, например:
struct Point {
var x: Float = 0
}
func foo() {
var p1 = Point()
var p2 = p1
p2.x += 1
print(p2.x)
}
foo()
Для начала компилятору даже не нужно создавать два экземпляра Point
— он может просто создать одну локальную переменную с плавающей запятой, инициализированную значением 1.0
, и распечатать ее.
Что касается передачи типов значений в качестве аргументов функций, для достаточно больших типов и (в случае структур) функций, которые используют достаточно своих свойств, компилятор -value-type-parameters-to-pass-by-reference" rel="noreferrer">может передавать их по ссылке, а не копировать. Затем вызываемый объект может сделать их копию только в случае необходимости, например, когда ему нужно работать с изменяемой копией.
В других случаях, когда структуры передаются по значению, компилятор также может использовать значения структуры, которая им нужна" rel="noreferrer">специализировать функции, чтобы копировать только те свойства, которые нужны функции.
Для следующего кода:
struct Point {
var x: Float = 0
var y: Float = 1
}
func foo(p: Point) {
print(p.x)
}
var p1 = Point()
foo(p: p1)
Предполагая, что foo(p:)
не встроен компилятором (в этом примере он будет встроен, но как только его реализация достигнет определенного размера, компилятор не решит, что это того стоит) - компилятор может специализировать функцию как:
func foo(px: Float) {
print(px)
}
foo(px: 0)
Он передает в функцию только значение свойства x
объекта Point
, тем самым снижая затраты на копирование свойства y
.
Поэтому компилятор сделает все возможное, чтобы уменьшить копирование типов значений. Но с таким количеством различных оптимизаций в различных обстоятельствах вы не можете просто свести оптимизированное поведение произвольных типов значений к простому копированию при записи.
person
Hamish
schedule
19.04.2017