class — cos'è, concetti chiave e principio di funzionamento in Swift

Autore: IT Sectr Pubblicato: 2026-06-17 Tempo di lettura: 8 min

Class è un tipo per riferimento (reference type) nel linguaggio Swift, le cui istanze vengono passate per riferimento anziché copiate durante l'assegnazione. A differenza delle struct, le classi supportano l'ereditarietà, la deinizializzazione e il conteggio automatico dei riferimenti ARC per la gestione della memoria. Secondo la Swift Programming Language Guide, 2026, class è necessaria per lavorare con i framework UI di Apple (UIKit, AppKit) e implementare pattern che richiedono identità condivisa dell'oggetto. La scelta tra class e struct è una delle decisioni architetturali chiave in Swift.

Punti chiave

  • Class — tipo per riferimento in Swift dove una variabile memorizza un riferimento a un oggetto nell'heap, non il valore stesso.
  • Ereditarietà — le classi possono ereditare proprietà e metodi da una classe genitore e sovrascriverli usando override.
  • ARC — il conteggio automatico dei riferimenti libera memoria quando non rimangono riferimenti forti a un oggetto.
  • Deinit — metodo speciale chiamato prima del rilascio di un oggetto per pulire le risorse.
  • Identità — gli operatori === e !== verificano se due variabili riferiscono la stessa istanza di classe.

Cos'è class in Swift?

Class — un tipo composto per riferimento in Swift che raggruppa proprietà e metodi in un'unica entità con supporto per ereditarietà e invio dinamico. A differenza di struct, un'istanza di classe viene creata nell'heap e una variabile memorizza un riferimento a questa istanza, non i dati stessi.

Quando si assegna una variabile di classe a un'altra variabile, entrambe riferiscono lo stesso oggetto in memoria. Le modifiche attraverso un riferimento sono visibili attraverso l'altro — questa è una proprietà fondamentale dei tipi per riferimento, utilizzata nei pattern di delega, osservazione e stato condiviso.

Secondo la Documentazione Apple Swift, le classi sono l'unico modo per lavorare con UIKit e AppKit, dove tutti i componenti UI ereditano da UIView e UIViewController. Inoltre, le classi sono necessarie per implementare pattern che richiedono identità dell'oggetto (due riferimenti a un oggetto) e ciclo di vita controllato.

Class come tipo per riferimento

La semantica del tipo per riferimento — la proprietà chiave delle classi. Quando si assegna una classe a una nuova variabile, Swift copia il riferimento, non i dati. Tutte le variabili che riferiscono la stessa istanza vedono il suo stato corrente e possono modificarlo. Questo comportamento differisce fondamentalmente dai tipi per valore, dove ogni variabile ottiene una copia indipendente.

swift
// Esempio di semantica del tipo per riferimento
class User {
    var name: String
    init(name: String) { self.name = name }
}

let user1 = User(name: "Alice")
let user2 = user1           // user2 — stesso riferimento
user2.name = "Bob"

print(user1.name)               // "Bob" — modificato tramite user2!

// Verifica dell'identità con l'operatore ===
print(user1 === user2)          // true — stesso oggetto

Gli operatori === (identità) e !== verificano se due variabili riferiscono la stessa istanza di classe. Questo differisce da == (uguaglianza), che confronta i valori delle proprietà. L'operatore === non è disponibile per struct — i tipi per valore non hanno identità.

Ereditarietà delle classi

Ereditarietà — un meccanismo mediante il quale una classe può adottare le proprietà e i metodi di una classe genitore. In Swift, una classe può ereditare solo da un genitore (ereditarietà singola), ma può implementare più protocolli. La parola chiave override consente di sovrascrivere un metodo o una proprietà ereditati.

Classe base e sottoclasse

Qualsiasi classe che non eredita da un'altra classe diventa automaticamente una classe base (da non confondere con NSObject). Una sottoclasse specifica il genitore con due punti dopo il suo nome. Se una sottoclasse sovrascrive un metodo del genitore, deve chiamare super.metodo() per preservare il comportamento del genitore — questo è un requisito del compilatore.

Classe finale

La parola chiave final prima di class impedisce l'ereditarietà. Il compilatore può ottimizzare le chiamate ai metodi di una classe finale attraverso l'invio statico, migliorando le prestazioni. Usa final per le classi non destinate all'estensione — documenta la tua intenzione e accelera il codice.

swift
// Esempio di ereditarietà delle classi in Swift
class Vehicle {
    var speed: Double = 0

    func description() -> String {
        "Speed: \(speed) km/h"
    }
}

// Car eredita da Vehicle
class Car: Vehicle {
    var brand: String = "Unknown"

    override func description() -> String {
        "\(brand) - \(speed) km/h"
    }
}

// Classe finale — impedisce l'ereditarietà
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"

L'ereditarietà è un meccanismo potente ma responsabile. Gerarchie profonde (più di 5 livelli) complicano la manutenzione e i test. Per il riutilizzo funzionale senza ereditarietà, usa i protocolli con extension e la programmazione orientata ai protocolli — un approccio che Apple promuove come alternativa alle gerarchie di classi profonde.

Deinizializzazione e gestione della memoria

Swift utilizza ARC (Conteggio Automatico dei Riferimenti) per la gestione della memoria delle classi. Ogni istanza di classe ha un contatore di riferimenti forti. Quando viene creato un nuovo riferimento forte, il contatore aumenta; quando viene distrutto, diminuisce. Quando il contatore raggiunge zero, la memoria viene liberata.

Deinit

Deinit — metodo chiamato automaticamente prima del rilascio di un'istanza di classe. Libera risorse: chiude file, annulla sottoscrizioni a notifiche, ferma timer. Deinit è disponibile solo per le classi — struct ed enum non lo hanno.

Riferimenti deboli e non posseduti

Per prevenire cicli di riferimenti forti (retain cycles), Swift fornisce riferimenti weak e unowned. Weak è un riferimento opzionale che diventa automaticamente nil quando l'oggetto viene rilasciato. Unowned non è opzionale ma riferisce un oggetto che ha la garanzia di vivere più a lungo del contesto attuale. Un tipico retain cycle si verifica in una relazione genitore-figlio: il figlio mantiene un riferimento forte al genitore.

swift
// Esempio di ARC e riferimento debole
class Parent {
    var name: String
    var child: Child?

    init(name: String) { self.name = name }
    deinit { print("\(name) deallocato") }
}

class Child {
    var name: String
    weak var parent: Parent?   // weak previene il ciclo di mantenimento

    init(name: String) { self.name = name }
    deinit { print("\(name) deallocato") }
}

var parent: Parent? = Parent(name: "Anna")
parent?.child = Child(name: "Mia")
parent?.child?.parent = parent

parent = nil  // Entrambi gli oggetti deallocati!
// Senza weak creerebbe un ciclo di mantenimento

Usa sempre weak per i riferimenti da un oggetto figlio al genitore e per le liste di cattura nelle closure. Usa unowned solo quando sei sicuro che l'oggetto sopravviverà al contesto attuale — l'uso errato di unowned può causare un crash quando si accede a memoria liberata.

Class vs Struct: differenze chiave

La scelta tra class e struct è una decisione architetturale che influisce su prestazioni, sicurezza dei thread e progettazione delle API. Considera la tabella delle differenze chiave per prendere la decisione giusta in ogni caso.

Caratteristicaclassstruct
TipoTipo per riferimentoTipo per valore (copia)
MemoriaHeap + ARCStack / inlined
EreditarietàSupporta (genitore singolo)Non supporta
DeinitDisponibileNon disponibile
Identità (===)SupportaNon supporta
MutatingNon richiesto (sempre mutating)Solo con mutating
Init memberwiseNon generatoGenerato automaticamente
Sicurezza threadNon garantita (stato condiviso)Garantita (copia)

Usa le classi quando hai bisogno di identità condivisa (più parti di codice che lavorano con un oggetto), ereditarietà o interazione con Objective-C runtime. Per tutto il resto, le struct sono preferibili — sono più veloci, più sicure nel codice multithread e non richiedono gestione della memoria.

Quando usare class in Swift

Nonostante la raccomandazione di Apple di usare struct per impostazione predefinita, le classi sono necessarie in diversi scenari specifici. Esaminiamo ciascuno con esempi pratici.

Componenti UIKit e AppKit

Tutti i componenti UI su iOS e macOS sono classi che ereditano da UIView (iOS) o NSView (macOS). Non puoi sostituire UIViewController con una struct — richiede ereditarietà e deinit per liberare risorse. Quando lavori con UIKit, usa classi per controller, viste e i loro delegati.

Singleton e stato condiviso

Il pattern Singleton (una istanza di classe per l'intera applicazione) richiede semantica di riferimento. I manager: NetworkManager, SettingsManager, AnalyticsService — sono tipicamente implementati come classi con una proprietà statica condivisa. Le struct non sono adatte perché ogni copia sarebbe un'istanza indipendente.

Iniezione di dipendenze e oggetti per riferimento

Quando un oggetto deve essere l'unica fonte di verità e viene passato tra moduli per riferimento — usa class. Questo si applica alla gestione dello stato, dove la modifica di un oggetto in un punto deve essere visibile in tutti i componenti dipendenti. Per ObservableObject in SwiftUI, le classi sono obbligatorie.

swift
// ObservableObject — class richiesta per SwiftUI
import SwiftUI

class AppViewModel: ObservableObject {
    @Published var isLoggedIn: Bool = false
    @Published var username: String = ""

    func login(user: String) {
        isLoggedIn = true
        username = user
    }
}

// Utilizzo in una vista SwiftUI
struct ContentView: View {
    @StateObject var viewModel = AppViewModel()

    var body: some View {
        Text(viewModel.isLoggedIn ? "Benvenuto" : "Accedi")
    }
}

Regola: se un oggetto deve avere identità (due riferimenti -> un oggetto), essere una singola istanza o funzionare con UIKit/Objective-C — scegli class. Se un oggetto contiene semplicemente dati — scegli struct.

Domande frequenti

Cos'è class in Swift?

Class è un tipo per riferimento in Swift che supporta ereditarietà, deinizializzazione e ARC per la gestione della memoria. Le istanze di classe sono memorizzate nell'heap e le variabili contengono un riferimento all'oggetto, non la sua copia.

In cosa class si differenzia da struct in Swift?

Class — tipo per riferimento (passato per riferimento), supporta ereditarietà e deinit. Struct — tipo per valore (copiato), non supporta ereditarietà, ma implementa protocolli e ottiene automaticamente init memberwise. Swift raccomanda struct come tipo predefinito.

Cos'è ARC in Swift?

ARC (Conteggio Automatico dei Riferimenti) — un meccanismo di gestione della memoria per le classi Swift. Ogni istanza ha un contatore di riferimenti forti. Quando il contatore raggiunge zero, la memoria viene liberata. I riferimenti weak e unowned prevengono i retain cycle tra oggetti.

Cos'è deinit in Swift?

Deinit — un metodo di classe chiamato automaticamente prima del rilascio della sua memoria. Utilizzato per chiudere file, annullare sottoscrizioni a notifiche e altre operazioni di pulizia. Deinit è disponibile solo nelle classi — le struct non lo hanno.

Quando usare class in Swift?

Usa class per componenti UI UIKit/AppKit, singleton, ObservableObject in SwiftUI, oggetti con identità condivisa (delegate, observer) e quando lavori con Objective-C runtime. Per modelli di dati, DTO e configurazioni, struct è preferibile.

Riepilogo

  • Class è un tipo per riferimento in Swift con semantica di passaggio per riferimento: tutte le variabili che puntano a un oggetto vedono il suo stato corrente e possono modificarlo.
  • ARC gestisce la memoria delle classi attraverso il conteggio dei riferimenti forti; weak e unowned prevengono i retain cycle nelle relazioni genitore-figlio e nelle closure.
  • Ereditarietà consente alle classi di riutilizzare e sovrascrivere il comportamento del genitore, ma gerarchie profonde (più di 5 livelli) complicano la manutenzione del codice.
  • Deinit viene eseguito quando un oggetto viene rilasciato ed è necessario per liberare risorse — file, timer, sottoscrizioni a NotificationCenter.
  • UIKit e AppKit richiedono classi per tutti i componenti UI — UIView, UIViewController, i loro delegati e data source.
  • ObservableObject in SwiftUI — class è obbligatoria perché SwiftUI traccia le modifiche attraverso @Published e @StateObject per riferimento.
  • Apple raccomanda struct come tipo predefinito per nuovi tipi di dati — usa class solo quando è richiesta identità, ereditarietà o integrazione con Objective-C.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche