W Swift typy wartości (struct, enum, tuple) mają tzw. value semantics: podczas przekazywania lub przypisywania zmiennej kopiowana jest cała zawartość – tworzy się niezależny egzemplarz. Pozwala to uniknąć wielu trudności związanych z shared state, charakterystycznych dla typu referencyjnego (class).
Jednak w celu optymalizacji pamięci kolekcje (np. Array, Dictionary, Set) stosują strategię copy-on-write: kopiowanie następuje tylko wtedy, gdy jeden z egzemplarzy zostaje zmieniony.
Przykład:
var a = [1, 2, 3] var b = a b.append(4) print(a) // [1, 2, 3] print(b) // [1, 2, 3, 4]
Tutaj tablica a nie zmieni się — chociaż początkowo istniała wspólna pamięć, przy zmianie b Swift stworzy osobną kopię danych.
Warto pamiętać: jeśli struktura zawiera typ referencyjny (np. klasę), value semantics mają zastosowanie tylko do samej struktury, a nie do wbudowanych obiektów referencyjnych.
Czy zawartość tablicy zmieni się, jeśli przekażemy ją do funkcji i wewnątrz tej funkcji ją zmodyfikujemy? Wyjaśnij różnicę w zachowaniu struct i class.
Odpowiedź z przykładem:
func mutate(_ arr: inout [Int]) { arr.append(100) } var source = [1, 2] mutate(&source) print(source) // [1, 2, 100]
Jeśli nie przekażemy przez inout, kopiowanie nastąpi automatycznie przy pierwszej zmianie wewnątrz funkcji i pierwotna tablica nie zmieni się. Dla klas kopiowanie nie ma miejsca – oryginalny obiekt zawsze się zmienia.
Historia
Programiści umieszczali obiekty referencyjne w tablicy struktur (struct), oczekując, że zmiana przez jedną strukturę nie wpłynie na inne egzemplarze. W rzeczywistości, zmieniając obiekty referencyjne w jednym miejscu, nieoczekiwanie zmieniały się one od razu wszędzie (shared state).
Historia
W projekcie zespołowym starano się zapewnić ochronę przed race condition, kopiując kolekcje przy każdym dostępie. Spowodowało to nieprzewidziane zużycie pamięci i spadek wydajności przy pracy z dużymi tablicami.
Historia
Młody programista próbował śledzić zmiany w tablicy, przez co przekazywał ją przez inout do kilku funkcji-obróbców jednocześnie. Kolejność modyfikacji stała się nieoczywista, co doprowadziło do niespotykanych zmian, błędów i problemów z synchronizacją.