Class — to typ referencyjny (reference type) w języku Swift, którego instancje są przekazywane przez referencję, a nie kopiowane przy przypisaniu. W przeciwieństwie do struktur, klasy obsługują dziedziczenie, deinicjalizację i automatyczne zliczanie referencji ARC do zarządzania pamięcią. Według Swift Programming Language Guide, 2026, class jest niezbędny do pracy z frameworkami UI Apple (UIKit, AppKit) i implementacji wzorców wymagających wspólnej tożsamości obiektu. Wybór między class a struct to jedna z kluczowych decyzji architektonicznych w Swift.
Najważniejsze
Class (klasa) — to złożony typ referencyjny w Swift, który grupuje właściwości i metody w jedną całość z obsługą dziedziczenia i dynamicznego wysyłania. W przeciwieństwie do struct, instancja klasy jest tworzona na stercie (heap), a zmienna przechowuje referencję do tej instancji, a nie same dane.
Gdy przypisujesz zmienną klasy do innej zmiennej, obie odnoszą się do tego samego obiektu w pamięci. Zmiany przez jedną referencję są widoczne przez drugą — to fundamentalna właściwość reference types, używana we wzorcach delegowania, obserwacji i współdzielonego stanu.
Według Apple Swift Documentation, klasy są jedynym sposobem pracy z UIKit i AppKit, gdzie wszystkie komponenty UI dziedziczą po UIView i UIViewController. Ponadto klasy są niezbędne do implementacji wzorców wymagających tożsamości obiektu (dwie referencje do jednego obiektu) i kontrolowanego czasu życia.
Semantyka reference type to kluczowa właściwość klas. Przy przypisaniu klasy do nowej zmiennej Swift kopiuje referencję, a nie dane. Wszystkie zmienne odnoszące się do jednej instancji widzą jej bieżący stan i mogą go zmieniać. To zachowanie zasadniczo różni się od value types, gdzie każda zmienna otrzymuje niezależną kopię.
// Przykład semantyki reference type
class User {
var name: String
init(name: String) { self.name = name }
}
let user1 = User(name: "Alice")
let user2 = user1 // user2 — ta sama referencja
user2.name = "Bob"
print(user1.name) // "Bob" — zmienione przez user2!
// Sprawdzenie tożsamości operatorem ===
print(user1 === user2) // true — ten sam obiekt
Operatory === (tożsamość) i !== sprawdzają, czy dwie zmienne odnoszą się do tego samego egzemplarza klasy. To odróżnia je od == (równość), które porównuje wartości właściwości. Dla struct operator === jest niedostępny — value types nie mają tożsamości.
Dziedziczenie — mechanizm, w którym klasa może przejąć właściwości i metody klasy nadrzędnej. W Swift klasa może dziedziczyć tylko od jednego rodzica (single inheritance), ale może implementować wiele protokołów. Słowo kluczowe override pozwala nadpisać odziedziczoną metodę lub właściwość.
Każda klasa, która nie dziedziczy z innej klasy, automatycznie staje się bazową (nie mylić z NSObject). Podklasa wskazuje rodzica przez dwukropek po nazwie. Jeśli podklasa nadpisuje metodę rodzica, musi wywołać super.metodę(), aby zachować działanie rodzica — to wymóg kompilatora.
Słowo kluczowe final przed class zabrania dziedziczenia. Kompilator może zoptymalizować wywołania metod final-klas poprzez static dispatch, co zwiększa wydajność. Używaj final dla klas, które nie są przeznaczone do rozszerzania — to dokumentuje twoją intencję i przyspiesza kod.
// Przykład dziedziczenia klas w Swift
class Vehicle {
var speed: Double = 0
func description() -> String {
"Speed: \(speed) km/h"
}
}
// Car dziedziczy po Vehicle
class Car: Vehicle {
var brand: String = "Unknown"
override func description() -> String {
"\(brand) - \(speed) km/h"
}
}
// Final class — zapobiega dziedziczeniu
final class ElectricCar: Car {
var batteryLevel: Double = 100
}
let tesla = ElectricCar()
tesla.brand = "Tesla"
tesla.speed = 120
print(tesla.description()) // "Tesla - 120.0 km/h"
Dziedziczenie klas to potężny, ale odpowiedzialny mechanizm. Głęboka hierarchia (5+ poziomów) utrudnia utrzymanie i testowanie. Do wielokrotnego użytku funkcjonalności bez dziedziczenia używaj protokołów z extension i programowania zorientowanego na protokoły — podejścia, które Apple promuje jako alternatywę dla głębokich hierarchii klas.
Swift używa ARC (Automatic Reference Counting) do zarządzania pamięcią klas. Każda instancja klasy ma licznik silnych referencji. Przy tworzeniu nowej silnej referencji licznik wzrasta; przy zniszczeniu — maleje. Gdy licznik osiąga zero, pamięć jest zwalniana.
Deinit — metoda wywoływana automatycznie przed zwolnieniem instancji klasy. W niej zwalnia się zasoby: zamyka pliki, wypisuje z powiadomień, zatrzymuje timery. Deinit występuje tylko w klasach — struktury i wyliczenia go nie mają.
Aby zapobiec cyklom silnych referencji (retain cycles), Swift udostępnia referencje weak i unowned. Weak — referencja opcjonalna, która automatycznie staje się nil przy zwolnieniu obiektu. Unowned — nieopcjonalna, ale odnosi się do obiektu, który gwarantowanie żyje dłużej niż bieżący. Typowy retain cycle powstaje w relacji parent-child: child przechowuje silną referencję do parent.
// Przykład ARC i słabej referencji
class Parent {
var name: String
var child: Child?
init(name: String) { self.name = name }
deinit { print("\(name) dealokowany") }
}
class Child {
var name: String
weak var parent: Parent? // weak zapobiega cyklowi referencji
init(name: String) { self.name = name }
deinit { print("\(name) dealokowany") }
}
var parent: Parent? = Parent(name: "Anna")
parent?.child = Child(name: "Mia")
parent?.child?.parent = parent
parent = nil // Oba obiekty dealokowane!
// Bez weak utworzyłoby cykl referencji
Zawsze używaj weak dla referencji z obiektu podrzędnego do nadrzędnego oraz dla list przechwytywania w domknięciach. Unowned używaj tylko gdy masz pewność, że obiekt przeżyje bieżący kontekst — nieprawidłowe użycie unowned może prowadzić do crasha przy dostępie do zwolnionej pamięci.
Wybór między class a struct to decyzja architektoniczna wpływająca na wydajność, bezpieczeństwo wątkowe i projekt API. Rozważmy tabelę kluczowych różnic, która pomoże podjąć właściwą decyzję w każdym konkretnym przypadku.
| Cecha | class | struct |
|---|---|---|
| Typ | Reference type (referencja) | Value type (kopia) |
| Pamięć | Sterta (heap) + ARC | Stos (stack) / inline |
| Dziedziczenie | Obsługuje (jeden rodzic) | Nie obsługuje |
| Deinit | Tak | Nie |
| Tożsamość (===) | Obsługuje | Nie obsługuje |
| Mutating | Nie wymagane (zawsze mutating) | Tylko z mutating |
| Memberwise init | Nie generowany | Generowany automatycznie |
| Bezpieczeństwo wątkowe | Nie gwarantowane (stan współdzielony) | Gwarantowane (kopiowanie) |
Używaj klas, gdy potrzebna jest wspólna tożsamość (wiele części kodu pracuje z jednym obiektem), dziedziczenie lub interakcja z Objective-C runtime. Do wszystkiego innego preferowane są struktury — są szybsze, bezpieczniejsze w kodzie wielowątkowym i nie wymagają zarządzania pamięcią.
Pomimo zalecenia Apple, aby domyślnie używać struct, klasy są niezbędne w kilku konkretnych scenariuszach. Omówmy każdy z praktycznymi przykładami.
Wszystkie komponenty UI w iOS i macOS to klasy dziedziczące po UIView (iOS) lub NSView (macOS). Nie możesz zastąpić UIViewController strukturą — wymaga on dziedziczenia i deinit do zwalniania zasobów. Przy pracy z UIKit używaj klas dla kontrolerów, widoków i ich delegatów.
Wzorzec Singleton (jeden egzemplarz klasy na całą aplikację) wymaga semantyki referencyjnej. Menedżery: NetworkManager, SettingsManager, AnalyticsService — zwykle implementowane jako klasy z shared static property. Struktury nie są odpowiednie, ponieważ każda kopia byłaby niezależnym egzemplarzem.
Gdy obiekt ma być jedynym źródłem prawdy (single source of truth) i jest przekazywany między modułami przez referencję — używaj class. Dotyczy to zarządzania stanem, gdzie zmiana obiektu w jednym miejscu powinna być widoczna we wszystkich zależnych komponentach. Dla ObservableObject w SwiftUI klasy są obowiązkowe.
// ObservableObject — class wymagany dla SwiftUI
import SwiftUI
class AppViewModel: ObservableObject {
@Published var isLoggedIn: Bool = false
@Published var username: String = ""
func login(user: String) {
isLoggedIn = true
username = user
}
}
// Użycie w widoku SwiftUI
struct ContentView: View {
@StateObject var viewModel = AppViewModel()
var body: some View {
Text(viewModel.isLoggedIn ? "Witaj" : "Zaloguj")
}
}
Zasada: jeśli obiekt ma mieć tożsamość (dwie referencje -> jeden obiekt), być jedynym egzemplarzem lub pracować z UIKit/Objective-C — wybieraj class. Jeśli obiekt po prostu przechowuje dane — wybieraj struct.
Często zadawane pytania
Class — to typ referencyjny (reference type) w Swift, który obsługuje dziedziczenie, deinicjalizację i ARC do zarządzania pamięcią. Instancje klas są przechowywane na stercie, a zmienne zawierają referencję do obiektu, a nie jego kopię.
Class — reference type (przekazywany przez referencję), obsługuje dziedziczenie i deinit. Struct — value type (kopiowany), nie obsługuje dziedziczenia, ale implementuje protokoły i otrzymuje memberwise init automatycznie. Swift zaleca struct jako typ domyślny.
ARC (Automatic Reference Counting) — mechanizm zarządzania pamięcią dla klas Swift. Każda instancja ma licznik silnych referencji. Gdy licznik spada do zera, pamięć jest zwalniana. Referencje weak i unowned zapobiegają cyklom silnych referencji między obiektami.
Deinit — metoda klasy wywoływana automatycznie przed zwolnieniem jej pamięci. Służy do zamykania plików, wypisywania z powiadomień i innych operacji końcowych. Deinit występuje tylko w klasach — struktury go nie mają.
Używaj class dla komponentów UI UIKit/AppKit, singleton, ObservableObject w SwiftUI, obiektów ze wspólną tożsamością (delegate, observer) i przy pracy z Objective-C runtime. Dla modeli danych, DTO i konfiguracji preferowana jest struct.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również