Historia pytania:
Shadowing (zacienianie) — zjawisko, w którym zmienna jest deklarowana pod tą samą nazwą w wewnętrznym obszarze widoczności, zasłaniając (zacieniając) zmienną z zewnętrznego obszaru. W pierwszych wersjach Swift kompilator był mniej surowy w odniesieniu do shadowingu, jednak wraz ze skomplikowaniem modelu języka, wyraźna identyfikacja takich przypadków stała się obowiązkowa, aby uniknąć błędów.
Problemy:
Shadowing utrudnia wsparcie kodu i generuje trudne do uchwycenia błędy — przypadkowe zadeklarowanie zmiennej o tej samej nazwie może prowadzić do tego, że odniesienie dotyczy nie tej zmiennej, której oczekuje programista. Jest to szczególnie krytyczne w przypadkach przekazywania zmiennych przez referencję oraz wewnątrz pętli lub zamknięć — często błędy objawiają się dopiero na etapie wykonania.
Rozwiązanie:
Swift dopuszcza shadowing zmiennych, jednak zaleca się unikanie zacieniania ważnych zmiennych/właściwości, stosowanie różnych nazw albo używanie wyraźnych referencji self/this. Szczególnie w inicjalizatorach struct/class zaleca się użycie self do rozróżnienia członka i parametru:
Przykład kodu:
struct User { let name: String init(name: String) { self.name = name // self zapewnia jasność przypisania } }
Kluczowe cechy:
Co się stanie przy shadowingu w pętlach i zamknięciach?
Jeżeli zadeklarujesz zmienną wewnątrz zamknięcia o tej samej nazwie co w kontekście zewnętrznym, shadowing ukryje zmienną zewnętrzną. Czasami prowadzi to do nieoczekiwanego zachowania, szczególnie przy wielowątkowości.
let value = 10 let closure = { let value = 20; print(value) } closure() // 20 print(value) // 10
Czy można zadeklarować stałą i zmienną o tej samej nazwie w jednym obszarze widoczności?
Nie. Swift nie zezwala na posiadanie var i let dla tej samej nazwy w obrębie jednego obszaru:
let x = 5 var x = 10 // Błąd: Nieprawidłowe ponowne zadeklarowanie 'x'
Jak uniknąć zamieszania ze shadowingiem właściwości klasy przy dziedziczeniu?
Jeżeli podklasa deklaruje właściwość o nazwie rodzica, dochodzi do shadowingu. Jednak przy dostępie przez super lub self zostanie wybrane odpowiednie właściwości. Lepiej unikać używania tych samych nazw bez potrzeby.
W klasie Account jeden z programistów przypadkowo użył zmiennej balance jako nazwy parametru funkcji i w samej funkcji znowu użył let balance = .... Z powodu shadowingu dokonano obliczenia nie na tym wartości, i funkcja zwracała błędny wynik, co ujawniło się dopiero na późnym etapie testowania.
Zalety:
Wady:
Cała drużyna postanowiła używać prefiksów (np. inputBalance) lub zawsze używać self do odniesień do właściwości. W rezultacie błędy związane z shadowingiem praktycznie zniknęły, a konserwacja kodu została uproszczona.
Zalety:
Wady: