Ich glaube, das ist was passiert. Wenn Sie
a = []
zuordnen weisen Sie a
auf ein neues Array. Das ursprüngliche Array existiert noch im Speicher und wenn Sie das tun:
b = 99
Sie den Original-Array ändern, nicht das neue Array, dass a
Referenzen.
Welche Beweise haben Sie, dass dies der Fall ist?
Betrachten Sie diese Änderungen an Ihrem Experiment:
Fall 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]"
Betrachten wir nun diese:
Fall 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]"
Natürlich ist das Ändern der einzelnen Elemente von a
nicht das Gleiche wie das Zuordnen eines Arrays zu a
.
In Fall 1, modifizierten wir die ursprüngliche Anordnung der Elemente in 4
, 5
und 6
, und die Zuordnung der b
Drehen verändert a[2]
wie erwartet.
Im Fall 2 zugewiesen wir [4, 5, 6]
-a
, die nicht die ursprünglichen Werte 4
geändert haben, 5
und 6
wies aber stattdessen a
in ein neues Array. Die Zuweisung von b
ändert sich nicht a[2]
in diesem Fall, da a
jetzt auf ein neues Array an einer anderen Speicherposition verweist. 3
Fall:
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]"
In Fall 3 sind wir in der Lage, die ursprüngliche Array a1
bevor die Zuweisung eines neuen Arrays zu a
zuzuweisen. Dies gibt uns einen Namen für das ursprüngliche Array. Wenn b
zugewiesen ist, wird a1[2]
geändert.
Aus den Kommentaren:
Ihre Antwort erklärt, warum die Zuordnung innerhalb der Funktion Werke b.Wenn foo jedoch endet und die Inout-Variablen zurückkopiert, sehe ich unter diesen Punkt nicht, wie schnell es möglich ist, die Freigabe des Originalarrays bis nach der & [2] Zuweisung aufzuschieben.
Es ist wahrscheinlich, dass dies ein Ergebnis der Referenzzählung durch ARC ist. Das ursprüngliche Array wird mit Bezug auf foo
übergeben und der Referenzzähler wird inkrementiert. Das ursprüngliche Array wird nicht freigegeben, bis der Referenzzähler am Ende von foo
dekrementiert ist.
Es scheint genauso haarig wie was die Docs bereits nicht zulassen - die gleiche Variable zweimal als Inout übergeben. Auch Ihr Fall3 ist überraschend. Sollte nicht die let a1 = eine Zeile Struktur/Wert Semantik und kopieren Sie einen Snapshot von das Array richtig dann?
Ja. Ich stimme zu, dass Fall 3 überraschend ist, aber es zeigt einiges von dem, was unter den Deckeln geschieht. Wenn Sie einer neuen Variablen ein Array zuweisen, erstellt Swift normalerweise nicht sofort eine Kopie. Stattdessen wird nur das zweite Array auf den ersten und den Referenzzähler inkrementiert. Dies geschieht aus Effizienzgründen. Es ist nur notwendig, eine Kopie zu erstellen, wenn eines der Arrays geändert wird. Wenn in diesem Fall a
geändert wird, behält a1
die ursprüngliche Kopie des Arrays im Speicher.
Das ist wirklich verwirrend; Ich verstehe nicht, warum a1 nicht 1,2,3 bekommen würde. Auch eine lassen Sie unveränderlich sein!
Die Tatsache, dass a1
modifiziert wird, wenn b
gesetzt ist, zeigt, dass a1
auf den Speicher des ursprünglichen Arrays zeigt. Swift scheint die Einstellung b
als Modifizierung a1
anscheinend nicht in Betracht zu ziehen. Vielleicht weil es bereits dafür gesorgt hat, dass a
änderbar war, als foo
mit &a[2]
aufgerufen wurde.
das ist interessant. Hast du es mit verschiedenen Optimierungsstufen versucht? INOUT-Parameter werden zuerst kopiert, dann verarbeitet und schließlich zurückkopiert. Wie ist die Kopie implementiert, wer weiß? – user3441734
ja ich kompiliert mit und ohne -O, dasselbe Ergebnis. schnell 2.1. – KarlP
Ich habe die gleichen Ergebnisse. vielleicht ist die Idee, das Ergebnis nicht zu kopieren, wenn 'out of bound' standardmäßig ist ... – user3441734