Suspended — ein angehaltener Zustand des iOS-App-Lebenszyklus, in dem die App im Speicher eingefroren ist, aber keinen Code ausführt. Wir zeigen, wie Suspended funktioniert, welche Risiken das Einfrieren einer App im Hintergrund mit sich bringt, wie iOS das Entladen von Suspended-Apps verwaltet und wie man State Restoration für eine nahtlose Wiederherstellung nach der Rückkehr aus Suspended implementiert.
Die wichtigsten Punkte
Suspended ist ein Zustand des iOS-App-Lebenszyklus, in dem die App im RAM des Geräts residiert, aber keinen Code ausführt. Dies ist der endgültige Zustand vor der vollständigen Beendigung: Die App wechselt nach Abschluss aller Hintergrundaufgaben oder nach Ablauf einer Zeitüberschreitung von Background zu Suspended. Im Suspended-Zustand ist die Anwendung vollständig eingefroren — alle Threads sind angehalten, Timer funktionieren nicht und es gibt keine Netzwerkaktivität.
Suspended ist eine einzigartige Funktion von iOS, die im standardmäßigen Android-Lebenszyklus nicht vorhanden ist. Der Grund liegt in den unterschiedlichen Prozessverwaltungsarchitekturen. iOS bewahrt das Abbild der App im Speicher auf (ähnlich dem Desktop-Ruhezustand), damit bei Rückkehr des Benutzers die Oberfläche sofort ohne Kaltstart wiederhergestellt werden kann. Android hat kein Suspended — ein Prozess existiert entweder und kann Code ausführen (Background) oder ist beendet (Not Running), obwohl Android die Thread-Ausführung über LMK anhalten kann.
Für den Benutzer sieht Suspended wie eine sofortige Wiederherstellung aus: Er wechselt zwischen Apps über den App Switcher, und jede App öffnet sich genau an der Stelle, an der er sie verlassen hat. Dies erweckt den Eindruck, dass alle Apps gleichzeitig ausgeführt werden. In Wirklichkeit sind die meisten von ihnen im Suspended-Zustand eingefroren. Ein Warmstart aus Suspended ist um ein Vielfaches schneller als ein Kaltstart aus Not Running, da der Code bereits in den Speicher geladen ist.
iOS überwacht den Zustand aller Anwendungen und entscheidet auf der Grundlage des verfügbaren Speichers, ob Suspended-Apps entladen werden sollen. Bei Speichermangel beginnt das System mit dem Entladen von Suspended-Apps, beginnend mit denen, die sich am längsten in diesem Zustand befinden. Reicht der Speicher immer noch nicht aus, wechselt das System Apps aus Background und Inactive in den Suspended-Zustand mit anschließendem Entladen. Dieser Prozess ist für den Benutzer völlig transparent — er sieht lediglich das App-Symbol im App Switcher, das bei Antippen einen Kaltstart auslöst.
| Eigenschaft | Suspended (iOS) | Background (iOS) | Background (Android) |
|---|---|---|---|
| Codeausführung | Nein | Ja (eingeschränkt) | Ja (eingeschränkt) |
| Im Speicher | Ja | Ja | Ja |
| CPU-Verbrauch | 0% | Niedrig | Niedrig |
| Warmstart | Ja — sofortige Wiederherstellung | Ja — über Inactive | Nein — Prozess könnte beendet worden sein |
| Zeitüberschreitung | Nein — kann stundenlang im Speicher bleiben | ~30 Sekunden (nach beginBackgroundTask) | Abhängig von API-Version |
| Entladen durch System | Bei Speichermangel | Bei kritischem Speichermangel | LMK (Low Memory Killer) |
| Rückkehr zur Arbeit | Aus App Switcher — sofort | Aus App Switcher — über Inactive | Kaltstart |
| State Restoration | Empfohlen | Nicht erforderlich | SavedStateHandle |
In iOS wird Suspended automatisch erreicht, nachdem alle Hintergrundaufgaben abgeschlossen sind. Das System ruft applicationDidEnterBackground auf, gibt Zeit zur Ausführung von beginBackgroundTask (ca. 30 Sekunden), pausiert dann zwangsweise alle Threads und versetzt die App in den Suspended-Zustand. Objekte im Speicher bleiben erhalten, aber es wird kein Code ausgeführt — die Anwendung ist in ihrem aktuellen Zustand eingefroren.
Ein kritisch wichtiger Punkt: applicationDidEnterBackground ist die letzte Methode, die garantiert vor Suspended aufgerufen wird. Danach erhält die App keine Benachrichtigungen über das Entladen des Speichers. Wenn der Benutzer oder das System die App im Suspended-Zustand beendet, werden weder applicationWillTerminate noch applicationDidEnterBackground erneut aufgerufen. Daher muss das gesamte Speichern von Daten in applicationDidEnterBackground erfolgen, nicht in applicationWillTerminate.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// Letzter garantierter Aufruf vor Suspended
func applicationDidEnterBackground(_ application: UIApplication) {
// Alles speichern, was das Entladen aus dem Speicher überleben muss
savePersistentState()
saveNavigationStack()
// Zusätzliche Zeit anfordern, wenn nötig
let task = application.beginBackgroundTask {
application.endBackgroundTask(task)
}
}
// Rückkehr aus Suspended — Warmstart
func applicationWillEnterForeground(_ application: UIApplication) {
// App war im Suspended-Zustand, Wiederaufnahme der Arbeit
print("Rückkehr aus Suspended oder Background")
}
// Vollständige Wiederherstellung nach dem Entladen aus dem Speicher
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Wenn dies ein Kaltstart nach dem Entladen aus Suspended ist —
// State Restoration wiederherstellen
return true
}
private func savePersistentState() {
UserDefaults.standard.set(Date(), forKey: "lastActiveDate")
}
private func saveNavigationStack() {
guard let rootVC = window?.rootViewController else { return }
// Aktuellen Navigations-Stack speichern
if let navController = rootVC as? UINavigationController {
let vcClasses = navController.viewControllers.map { type(of: $0) }
UserDefaults.standard.set(vcClasses.map { NSStringFromClass($0) }, forKey: "navStack")
}
}
}Der Code zeigt die kritisch wichtige Behandlung von Suspended in iOS. applicationDidEnterBackground ist der letzte garantierte Aufruf. Das gesamte Speichern von Daten sollte hier erfolgen: Benutzerzustand, Navigations-Stack, Entwürfe, Timer. applicationWillEnterForeground wird bei der Rückkehr aus Suspended oder Background aufgerufen. didFinishLaunchingWithOptions — nur bei Kaltstart, wenn die App nach Suspended aus dem Speicher entladen wurde.
In Android gibt es kein direktes Analogon zu iOS Suspended. Android friert Apps nicht unter Beibehaltung des Ausführungskontexts im Speicher ein. Stattdessen behält Android den Prozess entweder im Hintergrund oder beendet ihn. Allerdings wurde unter Android 11+ (API 30) ein Mechanismus namens App Freezer eingeführt, der die Ausführung von Hintergrundprozessen mit dem SIGSTOP-Signal aussetzt. Dies ist funktional ähnlich wie Suspended, jedoch mit wichtigen Unterschieden.
App Freezer ist Teil des Android-Speicherverwaltungssystems. Wenn eine App ohne aktive Benachrichtigungen längere Zeit im Hintergrund ist, sendet das System SIGSTOP und pausiert alle Threads. Wenn die App in den Vordergrund zurückkehrt, wird SIGCONT gesendet und die Ausführung fortgesetzt. Der Hauptunterschied zu iOS: App Freezer garantiert keine Zustandserhaltung — Daten im Speicher können verloren gehen, wenn der Prozess während des Einfrierens beendet wird.
Unter Android wird die Verwendung von SavedStateHandle im ViewModel zur automatischen Zustandsspeicherung bei jeder Prozessbeendigung empfohlen. SavedStateHandle speichert Daten in einem Bundle über onSaveInstanceState, das sowohl App Freezer als auch Process Death überlebt. Im Gegensatz zu iOS, wo das Entladen aus Suspended eine Ausnahmesituation ist, ist Process Death unter Android ein normales Verhalten, das immer erwartet werden sollte.
// SavedStateHandle — Rettung vor Process Death unter Android
class CheckoutViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// Zustand, der den Prozess sogar nach App Freezer überlebt
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
}
}
// Speichern in onStop für den Fall von App Freezer
class MainActivity : AppCompatActivity() {
override fun onStop() {
super.onStop()
// Daten speichern, die das Einfrieren überleben müssen
saveDraftData()
// Ressourcen freigeben, die im eingefrorenen Zustand nicht benötigt werden
releaseHeavyResources()
// Warnen, dass die App eingefroren wird
// (Protokollierung zum Debuggen)
Log.d("Lifecycle", "Activity stopped — möglicher App Freeze")
}
}Der Code zeigt den Ansatz zur Behandlung des Suspended-Analogons unter Android. SavedStateHandle im ViewModel speichert und stellt Daten während Process Death automatisch wieder her. onStop ist das letzte garantierte Ereignis vor einem möglichen App Freezer oder einer Prozessbeendigung. Der Zustand des Checkout-Formulars, die Artikelliste im Warenkorb — all diese Daten überleben das Einfrieren dank SavedStateHandle. Für schwere Ressourcen (Bitmaps, DB-Cursor) ist onStop der Ort, um Speicher freizugeben.
State Restoration ist ein integrierter iOS-Mechanismus zum Speichern und Wiederherstellen des UI-Zustands, nachdem die App aus dem Speicher entladen wurde. Wenn die App im Suspended-Zustand war und das System sie entladen hat, stellt State Restoration beim nächsten Kaltstart den Navigations-Stack, die Bildlaufposition, den Formularzustand und andere UI-Elemente wieder her. Der Benutzer kehrt zu demselben Bildschirm zurück, auf dem er sich befand.
State Restoration funktioniert über die Protokolle UIViewControllerRestoration und UIStateRestoring. Der Entwickler weist jedem ViewController und View, die wiederhergestellt werden sollen, eine restorationIdentifier zu. Beim Wechsel in den Hintergrund codiert iOS den Zustand dieser Objekte. Bei der Rückkehr nach dem Entladen erstellt iOS neue Objekte und decodiert den gespeicherten Zustand. Ohne State Restoration sieht der Benutzer nach einem Kaltstart einen leeren Bildschirm anstelle der Stelle, an der er aufgehört hat.
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
// Position nach dem Laden der Daten wiederherstellen
}
}
}
// AppDelegate — Aktivierung von State Restoration
func application(
_ application: UIApplication,
shouldSaveSecureApplicationState coder: NSCoder
) -> Bool {
return true
}
func application(
_ application: UIApplication,
shouldRestoreSecureApplicationState coder: NSCoder
) -> Bool {
return true
}Der Code zeigt die Implementierung von State Restoration in iOS. restorationIdentifier und restorationClass sind für jeden wiederherstellbaren ViewController erforderlich. encodeRestorableState/decodeRestorableState speichern und laden Daten über NSCoder. Im AppDelegate aktivieren shouldSaveSecureApplicationState und shouldRestoreSecureApplicationState die verschlüsselte Zustandsspeicherung. Ab iOS 12+ wird die Verwendung sicherer Codierung (NSSecureCoding) zum Schutz der Daten empfohlen.
Die erste Regel — gehen Sie niemals davon aus, dass die App aus Suspended zurückkehrt. Das System kann die App jederzeit entladen. Alle kritisch wichtigen Daten müssen vor dem Übergang zu Suspended im persistenten Speicher gesichert werden — also in applicationDidEnterBackground oder onStop. UserDefaults, Core Data, File Manager — geeignete Speicheroptionen. Der Arbeitsspeicher (Variablen, Eigenschaften) ist ein unzuverlässiger Speicher für Daten, die Suspended überleben müssen.
Die zweite Regel — geben Sie Ressourcen vor Suspended frei. Schließen Sie Dateideskriptoren, geben Sie GPU-Speicher frei (Metal, Core Graphics), schließen Sie Netzwerkverbindungen. Obwohl die App im Suspended-Zustand keine CPU verbraucht, sind belegte Ressourcen für andere Anwendungen blockiert. In iOS dürfen im Suspended-Zustand keine offenen Sockets gehalten werden — bei der Rückkehr aus Suspended könnten sie funktionsunfähig sein und Fehler verursachen.
Die dritte Regel — platzieren Sie keine zeitabhängige Logik, die auf die Rückkehr aus Suspended wartet. Timer, Callbacks und Netzwerkaktivität werden im Suspended-Zustand eingestellt. Wenn die App mehrere Stunden im Suspended-Zustand war, kann ein Timer bei der Rückkehr falsch auslösen. Überprüfen Sie die Gültigkeit der Daten bei der Rückkehr — der Cache könnte veraltet sein und das Autorisierungstoken könnte abgelaufen sein.
Die vierte Regel — verwenden Sie State Restoration für alle Bildschirme, insbesondere für Eingabeformulare, scrollbare Listen und Detailbildschirme. Ohne State Restoration sieht der Benutzer nach der Rückkehr aus einem entladenen Suspended den Startbildschirm der App anstelle der Stelle, an der er aufgehört hat. Dies verschlechtert die Benutzererfahrung und zwingt den Benutzer, Aktionen zu wiederholen.
import UIKit
// Überprüfung: Wurde die App aus dem Speicher entladen?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Überprüfen, ob ein gespeicherter Zustand existiert
if UserDefaults.standard.object(forKey: "navStack") != nil {
// Die App wurde aus Suspended entladen
// Zustand muss wiederhergestellt werden
restoreNavigationStack()
} else {
// Sauberer Kaltstart aus 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)
}
}
}Der Code zeigt die Praxis, festzustellen, ob die App aus Suspended entladen wurde. Die Überprüfung von UserDefaults auf das Vorhandensein eines gespeicherten Navigations-Stacks ermöglicht die Unterscheidung zwischen einem Kaltstart nach dem Entladen und einem sauberen Kaltstart. Im ersten Fall wird der Navigations-Stack wiederhergestellt, im zweiten Fall wird der Onboarding- oder Hauptbildschirm angezeigt. Dieser Ansatz ergänzt die integrierte State Restoration für Fälle, in denen NSCoder nicht ausreicht.
Häufig gestellte Fragen
Unbegrenzt — von einigen Sekunden bis zu mehreren Tagen. iOS hat kein Zeitlimit für Suspended. Die App bleibt so lange im Speicher, bis das System beschließt, sie aufgrund von Ressourcenknappheit zu entladen. In der Praxis bleiben Apps je nach RAM-Größe des Geräts und Anzahl der aktiven Anwendungen zwischen 15 Minuten und mehreren Stunden im Suspended-Zustand.
Nein. applicationWillTerminate wird nicht aufgerufen, wenn die App aus Suspended entladen wird. Das System gibt einfach Speicher frei, ohne die App zu benachrichtigen. Dies ist ein weiterer Grund, warum das gesamte Speichern von Daten in applicationDidEnterBackground erfolgen sollte. applicationWillTerminate wird nur aufgerufen, wenn der Benutzer die App manuell durch Herauswischen aus dem App Switcher beendet.
Es gibt kein direktes Analogon. Unter Android 11+ wurde App Freezer eingeführt, der Hintergrundprozesse über SIGSTOP anhält — dies ist funktional ähnlich zu Suspended. Allerdings sollten Android-Apps unter der Annahme entwickelt werden, dass Process Death jederzeit eintreten kann. Verwenden Sie SavedStateHandle im ViewModel und onSaveInstanceState, um den Zustand zu speichern, der sowohl App Freezer als auch Process Death überlebt.
In iOS gibt es keine direkte API zur Überprüfung. Eine indirekte Methode: Überprüfen Sie UserDefaults auf das Vorhandensein eines gespeicherten Zustands in didFinishLaunchingWithOptions. Wenn ein Zustand vorhanden ist — die App wurde aus Suspended entladen und startet kalt. Wenn kein Zustand vorhanden ist — ein sauberer Kaltstart. In SwiftUI kann ein Flag in scenePhase.background gespeichert und beim nächsten Start überprüft werden.
Beim Übergang zu Suspended erstellt iOS einen Snapshot — einen Screenshot der aktuellen App-Oberfläche. Dieser Screenshot wird im App Switcher und bei der Rückkehr zur App (als „Auftau“-Animation) angezeigt. Wenn die App vertrauliche Daten enthält, kann der Snapshot diese offenlegen. Verwenden Sie zum Schutz UIApplication.shouldSnapshotSecureApp (iOS 16+) oder legen Sie in applicationDidEnterBackground eine Unschärfe-Überlagerung an.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch