Suspended — geschorste toestand van de levenscyclus van een iOS-app, waarin deze in het geheugen is bevroren, maar geen code uitvoert. We laten zien hoe Suspended werkt, welke risico's het bevriezen van een app op de achtergrond met zich meebrengt, hoe iOS het ontladen van Suspended-apps beheert en hoe je state restoration implementeert voor naadloos herstel na terugkeer uit Suspended.
Belangrijkste punten
Suspended — de toestand van de levenscyclus van een iOS-app waarin deze zich in het werkgeheugen van het apparaat bevindt, maar geen code uitvoert. Dit is de laatste toestand vóór volledige beëindiging: de app gaat van Background naar Suspended na het voltooien van alle achtergrondtaken of na het verstrijken van de timeout. In Suspended is de app volledig bevroren — alle threads zijn onderbroken, timers werken niet, netwerkactiviteit vindt niet plaats.
Suspended — een unieke eigenschap van iOS die niet voorkomt in de standaard levenscyclus van Android. De reden is een andere architectuur van procesbeheer. iOS bewaart het beeld van de app in het geheugen (analoog aan hibernatie op een desktop) om bij terugkeer van de gebruiker de interface onmiddellijk te herstellen zonder cold start. Android heeft geen Suspended — het proces bestaat ofwel en kan code uitvoeren (Background), ofwel is beëindigd (Not Running), hoewel Android de uitvoering van threads kan onderbreken via LMK.
Voor de gebruiker lijkt Suspended op onmiddellijk herstel: hij schakelt tussen apps via App Switcher en elke app opent op dezelfde plek waar hij hem heeft achtergelaten. Dit wekt de illusie dat alle apps tegelijkertijd werken. In werkelijkheid zijn de meeste bevroren in Suspended. Hot start vanuit Suspended is vele malen sneller dan cold start vanuit Not Running, omdat de code al in het geheugen is geladen.
iOS volgt de toestand van alle apps en beslist over het ontladen van Suspended-apps op basis van beschikbaar geheugen. Bij geheugentekort begint het systeem Suspended-apps te ontladen, te beginnen met de apps die het langst in deze toestand verkeren. Als er nog steeds onvoldoende geheugen is, verplaatst het systeem apps van Background en Inactive naar Suspended en ontlaadt ze vervolgens. Dit proces is volledig transparant voor de gebruiker — hij ziet alleen het pictogram van de app in App Switcher, die bij klikken een cold start start.
| Kenmerk | Suspended (iOS) | Background (iOS) | Background (Android) |
|---|---|---|---|
| Code wordt uitgevoerd | Nee | Ja (beperkt) | Ja (beperkt) |
| In geheugen | Ja | Ja | Ja |
| CPU-verbruik | 0% | Laag | Laag |
| Hot start | Ja — onmiddellijk herstel | Ja — via Inactive | Nee — proces kan zijn gedood |
| Timeout | Nee — kan uren in geheugen blijven | ~30 seconden (na beginBackgroundTask) | Afhankelijk van API-versie |
| Ontladen door systeem | Bij geheugengebrek | Bij kritisch geheugengebrek | LMK (Low Memory Killer) |
| Terugkeer naar werk | Vanuit App Switcher — onmiddellijk | Vanuit App Switcher — via Inactive | Cold start |
| State Restoration | Aanbevolen | Niet vereist | SavedStateHandle |
In iOS wordt Suspended automatisch bereikt na voltooiing van alle achtergrondtaken. Het systeem roept applicationDidEnterBackground aan, geeft tijd voor het uitvoeren van beginBackgroundTask (ongeveer 30 seconden), waarna het alle threads geforceerd onderbreekt en de app naar de Suspended-toestand verplaatst. Objecten in het geheugen blijven behouden, maar er wordt geen code uitgevoerd — de app is bevroren in de huidige toestand.
Kritiek belangrijk moment: applicationDidEnterBackground — de laatste methode die gegarandeerd wordt aangeroepen vóór Suspended. Hierna ontvangt de app geen meldingen meer over ontladen uit het geheugen. Als de gebruiker of het systeem een app in Suspended doodt, wordt noch applicationWillTerminate, noch opnieuw applicationDidEnterBackground aangeroepen. Daarom moet alle gegevensopslag plaatsvinden in applicationDidEnterBackground, niet in applicationWillTerminate.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// Laatste gegarandeerde aanroep vóór Suspended
func applicationDidEnterBackground(_ application: UIApplication) {
// Alles opslaan wat het ontladen uit het geheugen moet overleven
savePersistentState()
saveNavigationStack()
// Extra tijd vragen indien nodig
let task = application.beginBackgroundTask {
application.endBackgroundTask(task)
}
}
// Terugkeer uit Suspended — hot start
func applicationWillEnterForeground(_ application: UIApplication) {
// App was in Suspended, keren terug naar werk
print("Terugkeer uit Suspended of Background")
}
// Volledig herstel na ontladen uit geheugen
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Als dit een cold start is na ontladen uit Suspended —
// state restoration herstellen
return true
}
private func savePersistentState() {
UserDefaults.standard.set(Date(), forKey: "lastActiveDate")
}
private func saveNavigationStack() {
guard let rootVC = window?.rootViewController else { return }
// Huidige navigatiestack opslaan
if let navController = rootVC as? UINavigationController {
let vcClasses = navController.viewControllers.map { type(of: $0) }
UserDefaults.standard.set(vcClasses.map { NSStringFromClass($0) }, forKey: "navStack")
}
}
}De code toont de kritieke verwerking van Suspended op iOS. applicationDidEnterBackground — de laatste gegarandeerde aanroep. Alle gegevensopslag moet hier plaatsvinden: gebruikersstatus, navigatiestack, concepten, timers. applicationWillEnterForeground wordt aangeroepen bij terugkeer uit Suspended of Background. didFinishLaunchingWithOptions — alleen bij cold start, wanneer de app uit het geheugen is ontladen na Suspended.
In Android bestaat geen directe analoog van iOS Suspended. Android bevriest apps niet in het geheugen met behoud van uitvoeringscontext. In plaats daarvan houdt Android het proces ofwel op de achtergrond (Background), ofwel beëindigt het (Not Running). Echter, op Android 11+ (API 30) is het mechanisme App Freezer verschenen, dat de uitvoering van achtergrondprocessen onderbreekt met het SIGSTOP-signaal. Dit is een functionele analoog van Suspended, maar met belangrijke verschillen.
App Freezer — onderdeel van het Android-geheugenbeheersysteem. Wanneer een app lang op de achtergrond staat en geen actieve meldingen heeft, stuurt het systeem SIGSTOP, waardoor alle threads worden onderbroken. Bij terugkeer van de app naar de voorgrond wordt SIGCONT gestuurd en wordt de uitvoering hervat. Belangrijk verschil met iOS: App Freezer garandeert niet het behoud van de toestand — gegevens in het geheugen kunnen verloren gaan als het proces tijdens het bevriezen wordt gedood.
Op Android wordt het gebruik van SavedStateHandle aanbevolen in ViewModel voor het automatisch opslaan van de toestand bij elke procesbeëindiging. SavedStateHandle slaat gegevens op in Bundle via onSaveInstanceState, die zowel App Freezer als Process Death overleeft. In tegenstelling tot iOS, waar ontladen uit Suspended een uitzonderlijke situatie is, is Process Death op Android normaal gedrag dat altijd verwacht moet worden.
// SavedStateHandle — redding van Process Death op Android
class CheckoutViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// Toestand die het proces overleeft, zelfs na 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
}
}
// Opslaan in onStop voor het geval van App Freezer
class MainActivity : AppCompatActivity() {
override fun onStop() {
super.onStop()
// Gegevens opslaan die het bevriezen moeten overleven
saveDraftData()
// Bronnen vrijgeven die niet nodig zijn in bevroren toestand
releaseHeavyResources()
// Waarschuwen dat de app wordt bevroren
// (Loggen voor foutopsporing)
Log.d("Lifecycle", "Activity gestopt — mogelijke App Freeze")
}
}De code toont de aanpak voor het verwerken van de Suspended-analoog op Android. SavedStateHandle in ViewModel slaat automatisch gegevens op en herstelt ze bij Process Death. onStop — de laatste gegarandeerde gebeurtenis vóór mogelijke App Freezer of procesbeëindiging. De status van het bestelformulier, de lijst met producten in de winkelwagen — al deze gegevens overleven het bevriezen dankzij SavedStateHandle. Voor zware bronnen (bitmaps, database-cursors) is onStop de plek om geheugen vrij te geven.
State Restoration — het ingebouwde iOS-mechanisme voor het opslaan en herstellen van de UI-toestand na het ontladen van de app uit het geheugen. Als de app in Suspended was en het systeem heeft deze ontladen, herstelt state restoration bij de volgende cold start de navigatiestack, scrollpositie, formulierstatus en andere UI-elementen. De gebruiker keert terug naar hetzelfde scherm waar hij was gebleven.
State Restoration werkt via de protocollen UIViewControllerRestoration en UIStateRestoring. De ontwikkelaar kent restorationIdentifier toe aan elke ViewController en View die hij wil herstellen. Bij het gaan naar Background codeert iOS de toestand van deze objecten. Na terugkeer uit ontladen maakt iOS nieuwe objecten aan en decodeert de opgeslagen toestand. Zonder state restoration ziet de gebruiker een leeg scherm na cold start in plaats van de plek waar hij was gebleven.
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
// Positie herstellen na het laden van gegevens
}
}
}
// AppDelegate — activering van State Restoration
func application(
_ application: UIApplication,
shouldSaveSecureApplicationState coder: NSCoder
) -> Bool {
return true
}
func application(
_ application: UIApplication,
shouldRestoreSecureApplicationState coder: NSCoder
) -> Bool {
return true
}De code toont de implementatie van State Restoration op iOS. restorationIdentifier en restorationClass zijn noodzakelijk voor elke te herstellen ViewController. encodeRestorableState/decodeRestorableState slaan gegevens op en laden ze via NSCoder. In AppDelegate schakelen shouldSaveSecureApplicationState en shouldRestoreSecureApplicationState gecodeerde opslag van de toestand in. Vanaf iOS 12+ wordt het gebruik van secure encoding (NSSecureCoding) aanbevolen voor gegevensbescherming.
Eerste regel — ga er nooit van uit dat de app terugkeert uit Suspended. Het systeem kan de app op elk moment ontladen. Alle kritiek belangrijke gegevens moeten vóór de overgang naar Suspended worden opgeslagen in permanente opslag — dat wil zeggen in applicationDidEnterBackground of onStop. UserDefaults, Core Data, File Manager — geschikte opslagplaatsen. Geheugen (variabelen, eigenschappen) — een onbetrouwbare opslag voor gegevens die Suspended moeten overleven.
Tweede regel — geef bronnen vrij vóór Suspended. Sluit bestandsdescriptoren, geef GPU-geheugen vrij (Metal, Core Graphics), sluit netwerkverbindingen. Hoewel de app geen CPU verbruikt in Suspended, worden bezette bronnen geblokkeerd voor andere apps. Op iOS in Suspended mogen geen open sockets worden gehouden — bij terugkeer uit Suspended kunnen ze niet werken, wat fouten veroorzaakt.
Derde regel — plaats geen tijdsafhankelijke logica in afwachting van terugkeer uit Suspended. Timers, callbacks en netwerkactiviteit stoppen in Suspended. Als de app enkele uren in Suspended is geweest, kan de timer bij terugkeer onjuist werken. Controleer de actualiteit van gegevens bij terugkeer — mogelijk is de cache verouderd en is het autorisatietoken verlopen.
Vierde regel — gebruik State Restoration voor alle schermen, vooral voor invoerformulieren, scrollbare lijsten en detailschermen. Zonder state restoration ziet de gebruiker na terugkeer uit ontladen Suspended het beginscherm van de app in plaats van de plek waar hij was gebleven. Dit verslechtert de gebruikerservaring en dwingt de gebruiker handelingen te herhalen.
import UIKit
// Controle: is de app uit het geheugen ontladen?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Controleren of er opgeslagen toestand is
if UserDefaults.standard.object(forKey: "navStack") != nil {
// App is ontladen uit Suspended
// Toestand moet worden hersteld
restoreNavigationStack()
} else {
// Schone cold start vanuit 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)
}
}
}De code toont de praktijk om te bepalen of de app uit Suspended is ontladen. Controle van UserDefaults op de aanwezigheid van een opgeslagen navigatiestack maakt het mogelijk om cold start na ontladen te onderscheiden van schone cold start. In het eerste geval wordt de navigatiestack hersteld, in het tweede wordt onboarding of het hoofdscherm getoond. Deze benadering vult de ingebouwde State Restoration aan voor gevallen waarin NSCoder niet voldoende is.
Veelgestelde vragen
Onbeperkt — van enkele seconden tot enkele dagen. iOS heeft geen timeout voor Suspended. De app blijft in het geheugen totdat het systeem besluit deze te ontladen vanwege gebrek aan bronnen. In de praktijk blijven apps 15 minuten tot enkele uren in Suspended, afhankelijk van de hoeveelheid RAM van het apparaat en het aantal actieve apps.
Nee. applicationWillTerminate wordt niet aangeroepen bij het ontladen van de app uit Suspended. Het systeem maakt eenvoudigweg het geheugen vrij zonder de app te waarschuwen. Dit is nog een reden waarom alle gegevensopslag in applicationDidEnterBackground moet plaatsvinden. applicationWillTerminate wordt alleen aangeroepen wanneer de gebruiker de app handmatig beëindigt door te vegen in App Switcher.
Er is geen directe analoog. Op Android 11+ is App Freezer verschenen, dat achtergrondprocessen onderbreekt via SIGSTOP — dit is functioneel vergelijkbaar met Suspended. Android-apps moeten echter worden ontworpen met het oog op Process Death op elk moment. Gebruik SavedStateHandle in ViewModel en onSaveInstanceState voor het opslaan van de toestand die zowel App Freezer als Process Death overleeft.
In iOS is er geen directe API om dit te controleren. Indirecte methode: controleer UserDefaults op de aanwezigheid van opgeslagen toestand in didFinishLaunchingWithOptions. Als de toestand bestaat — is de app uit Suspended ontladen en start hij koud. Als er geen toestand is — schone cold start. In SwiftUI kun je een flag opslaan in scenePhase.background en deze controleren bij de volgende start.
Bij de overgang naar Suspended maakt iOS een snapshot — een schermafbeelding van de huidige UI van de app. Deze afbeelding wordt getoond in App Switcher en bij terugkeer naar de app (als een
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook