Я считаю, что это то, что происходит. При назначении
a = []
вы указываете a
на новый массив. Исходный массив до сих пор существует в памяти, и когда вы делаете:
b = 99
вы модифицируя исходный массив, а не новый массив, a
ссылки.
Какие у вас есть данные об этом?
Рассмотрим модификацию эксперимента:
Случай 1:
func foo(inout a: [Int], inout b: Int) {
a[0] = 4
a[1] = 5
a[2] = 6
b = 99
}
var arr = [1,2,3]
foo(&arr, b: &arr[2])
print(arr) // prints "[4, 5, 99]"
Теперь рассмотрим еще одну:
Случай 2:
func foo(inout a: [Int], inout b: Int) {
a = [4, 5, 6]
b = 99
}
var arr = [1,2,3]
foo(&arr, b: &arr[2])
print(arr) // prints "[4, 5, 6]"
Очевидно, что изменение отдельных элементов a
- это не то же самое, что присвоение массива a
.
В случае 1, мы модифицировали исходный массив поворота элементов в 4
, 5
и 6
и назначение b
изменилось a[2]
, как ожидалось.
В случае 2 мы отнесли к [4, 5, 6]
a
который не изменяет исходные значения 4
, 5
и 6
, но вместо этого указал a
в новый массив. Назначение b
в этом случае не изменяется a[2]
, потому что a
теперь указывает на новый массив в другом месте в памяти.
Случай 3:
func foo(inout a: [Int], inout b: Int) {
let a1 = a
a = [4, 5, 6]
b = 99
print(a) // prints "[4, 5, 6]"
print(a1) // prints "[1, 2, 99]"
}
var arr = [1,2,3]
foo(&arr, b: &arr[2])
print(arr) // prints "[4, 5, 6]"
В случае 3, мы можем присвоить оригинальный массив a1
перед назначением нового массива a
. Это дает нам имя для исходного массива. Когда назначается b
, изменяется a1[2]
.
Из комментариев:
Ваш ответ объясняет, почему назначение б внутри функции работ.Однако, когда foo заканчивает и копирует inout переменные назад, на эта точка не показывает, как быстро известно, что отсрочить освобождение исходного массива до момента назначения & [2].
Скорее всего, это результат подсчета ссылок ARC. Исходный массив передается по ссылке foo
, и счетчик ссылок увеличивается. Исходный массив не освобождается, пока счетчик ссылок не уменьшится в конце foo
.
Кажется столь же волосатым, как то, что уже запретили документы, передавая ту же переменную дважды, что и inout. Также ваш случай3 удивителен. Должно ли не let a1 = a строка сделать семантику struct/value и скопировать моментальный снимок массив справа?
Да. Я согласен с тем, что случай 3 удивителен, но он показывает некоторые из того, что происходит под обложками. Обычно, когда вы назначаете один массив новой переменной, Swift немедленно не создает копию. Вместо этого он просто указывает второй массив на первый, и счетчик ссылок увеличивается. Это делается для эффективности. Необходимо только сделать копию, когда один из массивов будет изменен. В этом случае, когда изменяется a
, a1
сохраняет исходную копию массива в памяти.
Это действительно сбивает с толку; Я не понимаю, почему a1 не получит 1,2,3. Также let должен быть неизменным!
Тот факт, что a1
модифицируется, когда b
установлено, показывает, что a1
указывает на память исходного массива. Swift, по-видимому, не рассматривает установку b
в качестве модификации a1
. Возможно, потому, что он уже убедился, что a
был изменен, когда foo
был вызван с &a[2]
.
это интересно. вы пробовали его с разными уровнями оптимизации? Параметры INOUT сначала копируются, а затем обрабатываются и, наконец, копируются обратно. как копия реализована, кто знает? – user3441734
Да, я скомпилировал с и без -O, тот же результат. быстрый 2.1. – KarlP
У меня такие же результаты. может быть, идея заключается не в том, чтобы копировать результат, если по умолчанию «вне привязки» ... – user3441734