Sobes.tech
Middle+

W którym momencie i przy jakich warunkach zaczyna działać dispatching w Swift?

sobes.tech AI

Odpowiedź od AI

Dispatch metod (method dispatch) w Swift określa, jaki kod zostanie wykonany podczas wywołania metody obiektu. Wybór typu dispatchu odbywa się w momencie kompilacji kodu Swift.

Istnieją trzy główne typy dispatchu w Swift:

  1. Dispatch bezpośredni (Direct Dispatch):

    • Najszybszy.
    • Adres funkcji jest znany podczas kompilacji.
    • Domyślnie stosowany dla:
      • struct i enum (ponieważ nie obsługują dziedziczenia klas).
      • Metod final klas.
      • Metod private klas (internal i public nie obsługują bezpośredniego dispatchu).
      • Funkcji globalnych.
      • Metod statycznych.
      • Rozszerzeń.
  2. Dispatch przez tabelę (Table Dispatch):

    • Używany dla class, gdy potrzebny jest polimorfizm i dziedziczenie.
    • Adres konkretnej implementacji metody jest ustalany podczas wykonywania (runtime) za pomocą tabeli metod wirtualnych (vtable - Virtual Table).
    • Każda klasa ma własną vtable, zawierającą wskaźniki na implementacje metod.
    • Obsługuje dziedziczenie i nadpisywanie metod.
    class Animal { // Zastosuje dispatch przez tabelę
        func speak() {
            print("Nieznany dźwięk")
        }
    }
    
    class Dog: Animal { // Nadpisanie, używa vtable
        override func speak() {
            print("Hau")
        }
    }
    
  3. Dispatch przez tabelę świadków (Witness Table Dispatch):

    • Używany dla protokołów (protocol).
    • Podobny do dispatchu przez tabelę, ale używa tabeli świadków (witness table) do wyszukiwania implementacji metody protokołu dla konkretnego typu.
    • Umożliwia polimorfizm z protokołami.
    protocol Speakable { // Zastosuje Witness Table Dispatch
        func speak()
    }
    
    struct Cat: Speakable { // Implementacja protokołu
        func speak() {
            print("Miau")
        }
    }
    

Warunki wpływające na wybór:

  • Typ danych: struct, enum, class, protocol.
  • Modyfikatory dostępu: final, private, internal, public.
  • Dziedziczenie: Możliwość nadpisywania metod.
  • Zgodność z protokołami: Implementacja metod protokołu.
  • Kontekst wywołania: Wywołanie przez konkretny typ lub przez protokół/klasę bazową.

Kompilator Swift stara się używać dispatchu bezpośredniego tam, gdzie to możliwe, aby zoptymalizować wydajność, ale w razie potrzeby przełącza na dispatch przez tabelę lub tabelę świadków, aby obsługiwać polimorfizm i dziedziczenie. Jawne oznaczenie metod jako final lub private pozwala wymusić użycie dispatchu bezpośredniego.