Suspended — cos'è, il congelamento dell'app in background iOS

Autore: IT Sectr Pubblicato: 2026-03-03 Tempo di lettura: 11 min

Suspended — uno stato sospeso del ciclo di vita dell'applicazione iOS in cui l'app è congelata in memoria ma non esegue codice. Mostriamo come funziona Suspended, quali rischi comporta il congelamento dell'app in background, come iOS gestisce lo scaricamento delle app Suspended e come implementare state restoration per un recupero senza soluzione di continuità dopo il ritorno da Suspended.

Punti chiave

  • Suspended — l'app è congelata in memoria, il codice non viene eseguito, lo stack UI è preservato
  • iOS Suspended — uno stato unico che non esiste in Android; il processo esiste ma non è attivo
  • Scaricamento — quando la memoria è scarsa, le app Suspended vengono rimosse per prime, i dati in memoria vengono persi
  • State Restoration — meccanismo iOS per salvare e ripristinare lo stack UI dopo lo scaricamento
  • didEnterBackground — l'ultimo metodo garantito per essere chiamato prima di Suspended

Suspended — cos'è questo stato

Suspended è uno stato del ciclo di vita dell'applicazione iOS in cui l'app risiede nella RAM del dispositivo ma non esegue alcun codice. Questo è lo stato finale prima della terminazione completa: l'app passa da Background a Suspended dopo aver completato tutte le attività in background o dopo la scadenza di un timeout. In Suspended, l'applicazione è completamente congelata — tutti i thread sono sospesi, i timer non funzionano e non c'è attività di rete.

Suspended è una caratteristica unica di iOS, assente nel ciclo di vita standard di Android. Il motivo risiede nelle diverse architetture di gestione dei processi. iOS preserva l'immagine dell'app in memoria (simile alla ibernazione del desktop) in modo che quando l'utente torna, l'interfaccia possa essere istantaneamente ripristinata senza un avvio a freddo. Android non ha Suspended — un processo o esiste e può eseguire codice (Background) o è terminato (Not Running), sebbene Android possa sospendere l'esecuzione dei thread tramite LMK.

Per l'utente, Suspended sembra un ripristino istantaneo: passa da un'app all'altra tramite App Switcher e ogni app si apre esattamente dove l'aveva lasciata. Questo crea l'illusione che tutte le app funzionino simultaneamente. In realtà, la maggior parte di esse sono congelate in Suspended. Un avvio a caldo da Suspended è molte volte più veloce di un avvio a freddo da Not Running, perché il codice è già caricato in memoria.

Come il sistema gestisce Suspended

iOS monitora lo stato di tutte le applicazioni e decide di scaricare le app Suspended in base alla memoria disponibile. Quando la memoria è scarsa, il sistema inizia a scaricare le app Suspended, iniziando da quelle che sono in questo stato da più tempo. Se la memoria è ancora insufficiente, il sistema transita le app da Background e Inactive a Suspended con successivo scaricamento. Questo processo è completamente trasparente per l'utente — vede semplicemente l'icona dell'app nell'App Switcher che, al tocco, avvia un avvio a freddo.

CaratteristicaSuspended (iOS)Background (iOS)Background (Android)
Esegue codiceNoSì (limitato)Sì (limitato)
In memoria
Consumo CPU0%BassoBasso
Avvio a caldoSì — ripristino istantaneoSì — tramite InactiveNo — il processo potrebbe essere stato terminato
TimeoutNo — può stare in memoria per ore~30 secondi (dopo beginBackgroundTask)Dipende dalla versione dell'API
Scaricamento dal sistemaQuando la memoria è scarsaQuando la memoria è criticamente scarsaLMK (Low Memory Killer)
Ritorno al lavoroDa App Switcher — istantaneamenteDa App Switcher — tramite InactiveAvvio a freddo
State RestorationRaccomandatoNon richiestoSavedStateHandle

Suspended in iOS: il meccanismo di congelamento

In iOS, Suspended viene raggiunto automaticamente dopo il completamento di tutte le attività in background. Il sistema chiama applicationDidEnterBackground, dà tempo per eseguire beginBackgroundTask (circa 30 secondi), quindi sospende forzatamente tutti i thread e transita l'app in Suspended. Gli oggetti in memoria vengono preservati, ma nessun codice viene eseguito — l'applicazione è congelata nel suo stato attuale.

Un punto criticamente importante: applicationDidEnterBackground è l'ultimo metodo garantito per essere chiamato prima di Suspended. Dopo questo, l'app non riceve alcuna notifica sullo scaricamento della memoria. Se l'utente o il sistema termina l'app mentre è in Suspended, né applicationWillTerminate né applicationDidEnterBackground vengono chiamati di nuovo. Pertanto, tutto il salvataggio dei dati deve avvenire in applicationDidEnterBackground, non in applicationWillTerminate.

swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    // Ultima chiamata garantita prima di Suspended
    func applicationDidEnterBackground(_ application: UIApplication) {
        // Salvare tutto ciò che deve sopravvivere allo scaricamento della memoria
        savePersistentState()
        saveNavigationStack()

        // Richiedere tempo extra se necessario
        let task = application.beginBackgroundTask {
            application.endBackgroundTask(task)
        }
    }

    // Ritorno da Suspended — avvio a caldo
    func applicationWillEnterForeground(_ application: UIApplication) {
        // L'app era in Suspended, ripresa del lavoro
        print("Ritorno da Suspended o Background")
    }

    // Ripristino completo dopo lo scaricamento della memoria
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Se questo è un avvio a freddo dopo lo scaricamento da Suspended —
        // ripristinare state restoration
        return true
    }

    private func savePersistentState() {
        UserDefaults.standard.set(Date(), forKey: "lastActiveDate")
    }

    private func saveNavigationStack() {
        guard let rootVC = window?.rootViewController else { return }
        // Salvare lo stack di navigazione corrente
        if let navController = rootVC as? UINavigationController {
            let vcClasses = navController.viewControllers.map { type(of: $0) }
            UserDefaults.standard.set(vcClasses.map { NSStringFromClass($0) }, forKey: "navStack")
        }
    }
}

Il codice mostra la gestione criticamente importante di Suspended su iOS. applicationDidEnterBackground è l'ultima chiamata garantita. Tutto il salvataggio dei dati dovrebbe avvenire qui: stato utente, stack di navigazione, bozze, timer. applicationWillEnterForeground viene chiamato al ritorno da Suspended o Background. didFinishLaunchingWithOptions — solo all'avvio a freddo, quando l'app è stata scaricata dalla memoria dopo Suspended.

Suspended in Android — esiste un analogo

In Android, non esiste un analogo diretto di Suspended di iOS. Android non congela le app in memoria preservando il contesto di esecuzione. Invece, Android mantiene il processo in background o lo termina. Tuttavia, su Android 11+ (API 30), è stato introdotto un meccanismo chiamato App Freezer, che sospende l'esecuzione dei processi in background utilizzando il segnale SIGSTOP. Questo è funzionalmente simile a Suspended, ma con importanti differenze.

App Freezer fa parte del sistema di gestione della memoria di Android. Quando un'app rimane a lungo in background senza notifiche attive, il sistema invia SIGSTOP, sospendendo tutti i thread. Quando l'app torna in primo piano, viene inviato SIGCONT e l'esecuzione riprende. La differenza principale con iOS: App Freezer non garantisce la conservazione dello stato — i dati in memoria possono essere persi se il processo viene terminato durante il congelamento.

Su Android, si raccomanda di utilizzare SavedStateHandle in ViewModel per la conservazione automatica dello stato durante qualsiasi terminazione del processo. SavedStateHandle salva i dati in un Bundle tramite onSaveInstanceState, che sopravvive sia ad App Freezer che a Process Death. A differenza di iOS, dove lo scaricamento da Suspended è una situazione eccezionale, su Android Process Death è un comportamento normale che dovrebbe essere sempre previsto.

kotlin
// SavedStateHandle — salvezza da Process Death su Android
class CheckoutViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    // Stato che sopravvive al processo anche dopo App Freezer
    var currentStep: MutableLiveData<Int> =
        savedStateHandle.getLiveData("checkout_step", 1)

    var cartItems: MutableLiveData<List<CartItem>> =
        savedStateHandle.getLiveData("cart_items", emptyList())

    fun proceedToNextStep() {
        currentStep.value = (currentStep.value ?: 0) + 1
    }

    fun addToCart(item: CartItem) {
        val updatedList = (cartItems.value ?: emptyList()) + item
        cartItems.value = updatedList
        savedStateHandle["cart_items"] = updatedList
    }
}

// Salvare in onStop in caso di App Freezer
class MainActivity : AppCompatActivity() {

    override fun onStop() {
        super.onStop()
        // Salvare dati che devono sopravvivere al congelamento
        saveDraftData()
        // Liberare risorse non necessarie in stato congelato
        releaseHeavyResources()
        // Avvisare che l'app verrà congelata
        // (Registrazione per debug)
        Log.d("Lifecycle", "Activity stopped — possibile App Freeze")
    }
}

Il codice mostra l'approccio per gestire l'analogo di Suspended su Android. SavedStateHandle in ViewModel salva e ripristina automaticamente i dati durante Process Death. onStop è l'ultimo evento garantito prima di un possibile App Freezer o terminazione del processo. Lo stato del modulo di pagamento, la lista degli articoli nel carrello — tutti questi dati sopravvivono al congelamento grazie a SavedStateHandle. Per le risorse pesanti (bitmap, cursori DB), onStop è il posto per liberare memoria.

State Restoration: recupero dopo Suspended

State Restoration è un meccanismo integrato di iOS per salvare e ripristinare lo stato dell'UI dopo che l'app è stata scaricata dalla memoria. Se l'app era in Suspended e il sistema l'ha scaricata, al prossimo avvio a freddo state restoration ripristina lo stack di navigazione, la posizione di scorrimento, lo stato del modulo e altri elementi UI. L'utente torna allo stesso schermo dove si era fermato.

State Restoration funziona tramite i protocolli UIViewControllerRestoration e UIStateRestoring. Lo sviluppatore assegna un restorationIdentifier a ogni ViewController e View che desidera ripristinare. Andando in background, iOS codifica lo stato di questi oggetti. Al ritorno dopo uno scaricamento, iOS crea nuovi oggetti e decodifica lo stato salvato. Senza state restoration, l'utente vedrà uno schermo vuoto dopo un avvio a freddo invece del punto in cui si era fermato.

swift
import UIKit

class DetailViewController: UIViewController {

    var itemID: String = ""
    var scrollPosition: CGPoint = .zero

    override func viewDidLoad() {
        super.viewDidLoad()
        restorationIdentifier = "DetailViewController"
        restorationClass = type(of: self)
    }

    override func encodeRestorableState(with coder: NSCoder) {
        super.encodeRestorableState(with: coder)
        coder.encode(itemID, forKey: "itemID")
        coder.encode(scrollPosition, forKey: "scrollPosition")
    }

    override func decodeRestorableState(with coder: NSCoder) {
        super.decodeRestorableState(with: coder)
        if let savedID = coder.decodeObject(forKey: "itemID") as? String {
            itemID = savedID
            loadItem()
        }
        if let savedPosition = coder.decodeCGPoint(forKey: "scrollPosition") {
            scrollPosition = savedPosition
            // Ripristinare posizione dopo il caricamento dei dati
        }
    }
}

// AppDelegate — attivazione di State Restoration
func application(
    _ application: UIApplication,
    shouldSaveSecureApplicationState coder: NSCoder
) -> Bool {
    return true
}

func application(
    _ application: UIApplication,
    shouldRestoreSecureApplicationState coder: NSCoder
) -> Bool {
    return true
}

Il codice mostra l'implementazione di State Restoration su iOS. restorationIdentifier e restorationClass sono necessari per ogni ViewController ripristinabile. encodeRestorableState/decodeRestorableState salvano e caricano i dati tramite NSCoder. In AppDelegate, shouldSaveSecureApplicationState e shouldRestoreSecureApplicationState abilitano il salvataggio crittografato dello stato. Su iOS 12+, si raccomanda di utilizzare la codifica sicura (NSSecureCoding) per la protezione dei dati.

Migliori pratiche per lavorare con Suspended

La prima regola — non dare mai per scontato che l'app tornerà da Suspended. Il sistema può scaricare l'app in qualsiasi momento. Tutti i dati criticamente importanti devono essere salvati in memoria persistente prima della transizione a Suspended — cioè in applicationDidEnterBackground o onStop. UserDefaults, Core Data, File Manager — opzioni di archiviazione adatte. La memoria (variabili, proprietà) è un archivio inaffidabile per i dati che devono sopravvivere a Suspended.

La seconda regola — libera le risorse prima di Suspended. Chiudi i descrittori di file, libera la memoria GPU (Metal, Core Graphics), chiudi le connessioni di rete. Sebbene l'app non consumi CPU in Suspended, le risorse occupate rimangono bloccate per altre applicazioni. Su iOS, non si possono mantenere socket aperti in Suspended — al ritorno da Suspended, potrebbero non essere funzionali, causando errori.

La terza regola — non posizionare logica dipendente dal tempo in attesa del ritorno da Suspended. I timer, i callback e l'attività di rete cessano in Suspended. Se l'app è stata in Suspended per diverse ore, un timer potrebbe attivarsi in modo errato al ritorno. Controlla la validità dei dati al ritorno — la cache potrebbe essere obsoleta e il token di autorizzazione potrebbe essere scaduto.

La quarta regola — usa State Restoration per tutti gli schermi, specialmente moduli di input, elenchi scorrevoli e schermi di dettaglio. Senza state restoration, dopo essere tornato da un Suspended scaricato, l'utente vedrà lo schermo iniziale dell'app invece del punto in cui si era fermato. Questo degrada l'esperienza utente e costringe l'utente a ripetere le azioni.

swift
import UIKit

// Verifica: l'app è stata scaricata dalla memoria?
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    // Verificare se esiste stato salvato
    if UserDefaults.standard.object(forKey: "navStack") != nil {
        // L'app è stata scaricata da Suspended
        // Deve ripristinare lo stato
        restoreNavigationStack()
    } else {
        // Avvio a freddo pulito da Not Running
        showOnboardingIfNeeded()
    }
    return true
}

private func restoreNavigationStack() {
    guard let savedStack = UserDefaults.standard.array(forKey: "navStack") as? [String],
          let navController = window?.rootViewController as? UINavigationController
    else { return }

    for vcClassName in savedStack {
        if let vcClass = NSClassFromString(vcClassName) as? UIViewController.Type {
            let vc = vcClass.init()
            navController.pushViewController(vc, animated: false)
        }
    }
}

Il codice mostra la pratica per determinare se l'app è stata scaricata da Suspended. Controllare UserDefaults per la presenza di uno stack di navigazione salvato permette di distinguere un avvio a freddo dopo lo scaricamento da un avvio a freddo pulito. Nel primo caso, lo stack di navigazione viene ripristinato; nel secondo, viene mostrata la schermata di onboarding o principale. Questo approccio completa State Restoration integrato per i casi in cui NSCoder non è sufficiente.

Domande frequenti

Quanto tempo può rimanere un'app in Suspended?

Illimitato — da pochi secondi a diversi giorni. iOS non ha timeout per Suspended. L'app rimarrà in memoria fino a quando il sistema non deciderà di scaricarla per mancanza di risorse. Nella pratica, le app rimangono in Suspended da 15 minuti a diverse ore, a seconda della RAM del dispositivo e del numero di applicazioni attive.

Viene chiamato applicationWillTerminate quando si scarica da Suspended?

No. applicationWillTerminate non viene chiamato quando l'app viene scaricata da Suspended. Il sistema libera semplicemente la memoria senza avvisare l'applicazione. Questo è un ulteriore motivo per cui tutto il salvataggio dei dati deve avvenire in applicationDidEnterBackground. applicationWillTerminate viene chiamato solo quando l'utente termina manualmente l'app facendola scorrere fuori dall'App Switcher.

Android ha un analogo di Suspended?

Non esiste un analogo diretto. Su Android 11+, è stato introdotto App Freezer, che sospende i processi in background tramite SIGSTOP — questo è funzionalmente simile a Suspended. Tuttavia, le app Android dovrebbero essere progettate presupponendo Process Death in qualsiasi momento. Usa SavedStateHandle in ViewModel e onSaveInstanceState per salvare lo stato che sopravviverà sia ad App Freezer che a Process Death.

Come verificare se l'app era in Suspended?

In iOS non esiste un'API diretta per verificarlo. Un metodo indiretto: controllare UserDefaults per la presenza di stato salvato in didFinishLaunchingWithOptions. Se lo stato esiste — l'app è stata scaricata da Suspended e sta avviandosi a freddo. Se lo stato non esiste — è un avvio a freddo pulito. In SwiftUI, puoi salvare un flag in scenePhase.background e controllarlo al prossimo avvio.

Cos'è uno snapshot nel contesto di Suspended?

Durante la transizione a Suspended, iOS scatta uno snapshot — un screenshot dell'UI corrente dell'app. Questo screenshot viene mostrato nell'App Switcher e quando si torna all'app (come animazione di “scongelamento”). Se l'app contiene dati riservati, lo snapshot potrebbe esporli. Per proteggerti, usa UIApplication.shouldSnapshotSecureApp (iOS 16+) o applica una sovrapposizione di sfocatura in applicationDidEnterBackground.

Riepilogo

  • Suspended — l'app è congelata nella memoria iOS, il codice non viene eseguito, ma lo stack UI è preservato per il ripristino istantaneo
  • Unicità di iOS — Suspended non esiste in Android; Android usa App Freezer (SIGSTOP) come analogo parziale
  • Scaricamento — il sistema scarica le app Suspended come prima priorità quando la memoria è scarsa, senza notifica
  • Salvataggio — applicationDidEnterBackground è l'ultimo metodo garantito; tutti i dati dovrebbero essere salvati qui
  • State Restoration — meccanismo basato su NSCoder per il ripristino automatico dell'UI dopo lo scaricamento da Suspended
  • Alternativa Android — SavedStateHandle + onSaveInstanceState per sopravvivere a Process Death
  • Snapshot — iOS scatta uno screenshot durante Suspended; i dati riservati devono essere nascosti tramite sovrapposizione di sfocatura o snapshot sicuro

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