Suspended — avstängt tillstånd i livscykeln för iOS-appen, där den är frusen i minnet men inte utför någon kod. Vi visar hur Suspended fungerar, vilka risker frysning av appen i bakgrunden medför, hur iOS hanterar urladdning av Suspended-appar och hur man implementerar state restoration för sömlös återställning efter återkomst från Suspended.
Huvudpunkter
Suspended — tillstånd i livscykeln för iOS-appen där den befinner sig i enhetens operativminne men inte utför någon kod. Detta är det slutgiltiga tillståndet före fullständig avslutning: appen går över till Suspended från Background efter att alla bakgrundsuppgifter har slutförts eller efter timeout. I Suspended är appen helt frusen — alla trådar är avstängda, timer fungerar inte, nätverksaktivitet saknas.
Suspended — en unik funktion i iOS som inte finns i den vanliga livscykeln för Android. Orsaken är en annan arkitektur för processhantering. iOS bevarar bilden av appen i minnet (analogt med viloläge på en stationär dator) för att vid användarens återkomst omedelbart återställa gränssnittet utan cold start. Android har inte Suspended — processen finns antingen och kan utföra kod (Background) eller har avslutats (Not Running), även om Android kan stoppa utförandet av trådar via LMK.
För användaren ser Suspended ut som omedelbar återställning: han växlar mellan appar via App Switcher och varje app öppnas på samma ställe där han lämnade den. Detta skapar illusionen att alla appar körs samtidigt. I verkligheten är de flesta av dem frusna i Suspended. Varmstart från Suspended är många gånger snabbare än cold start från Not Running, eftersom koden redan är laddad i minnet.
iOS övervakar tillståndet för alla appar och fattar beslut om urladdning av Suspended-appar baserat på tillgängligt minne. När minnet är otillräckligt börjar systemet ladda ur Suspended-appar, med början från de som varit längst i detta tillstånd. Om minnet fortfarande är otillräckligt flyttar systemet appar från Background och Inactive till Suspended och laddar sedan ur dem. Denna process är helt transparent för användaren — han ser bara appikonen i App Switcher, som vid klick startar en cold start.
| Egenskap | Suspended (iOS) | Background (iOS) | Background (Android) |
|---|---|---|---|
| Kod utförs | Nej | Ja (begränsat) | Ja (begränsat) |
| I minnet | Ja | Ja | Ja |
| CPU-förbrukning | 0% | Låg | Låg |
| Varmstart | Ja — omedelbar återställning | Ja — via Inactive | Nej — processen kan ha dödats |
| Tidsgräns | Nej — kan vara i minnet i timmar | ~30 sekunder (efter beginBackgroundTask) | Beror på API-version |
| Urladdning av systemet | Vid minnesbrist | Vid kritisk minnesbrist | LMK (Low Memory Killer) |
| Återgång till arbete | Från App Switcher — omedelbart | Från App Switcher — via Inactive | Cold start |
| State Restoration | Rekommenderas | Krävs inte | SavedStateHandle |
I iOS uppnås Suspended automatiskt efter att alla bakgrundsuppgifter har slutförts. Systemet anropar applicationDidEnterBackground, ger tid för att utföra beginBackgroundTask (cirka 30 sekunder), varefter det tvångsvis stoppar alla trådar och flyttar appen till Suspended-tillståndet. Objekt i minnet bevaras, men ingen kod utförs — appen är frusen i sitt aktuella tillstånd.
Kritiskt viktigt ögonblick: applicationDidEnterBackground — den sista metoden som garanterat anropas före Suspended. Efter detta får appen inga meddelanden om urladdning från minnet. Om användaren eller systemet dödar en app som befinner sig i Suspended, anropas varken applicationWillTerminate eller applicationDidEnterBackground igen. Därför måste all datasparning ske i applicationDidEnterBackground, inte i applicationWillTerminate.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// Sista garanterade anropet före Suspended
func applicationDidEnterBackground(_ application: UIApplication) {
// Sparar allt som måste överleva urladdning från minnet
savePersistentState()
saveNavigationStack()
// Begär extra tid om det behövs
let task = application.beginBackgroundTask {
application.endBackgroundTask(task)
}
}
// Återkomst från Suspended — varmstart
func applicationWillEnterForeground(_ application: UIApplication) {
// Appen var i Suspended, återgår till arbete
print("Återkomst från Suspended eller Background")
}
// Fullständig återställning efter urladdning från minnet
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Om detta är cold start efter urladdning från Suspended —
// återställer state restoration
return true
}
private func savePersistentState() {
UserDefaults.standard.set(Date(), forKey: "lastActiveDate")
}
private func saveNavigationStack() {
guard let rootVC = window?.rootViewController else { return }
// Sparar aktuell navigeringsstack
if let navController = rootVC as? UINavigationController {
let vcClasses = navController.viewControllers.map { type(of: $0) }
UserDefaults.standard.set(vcClasses.map { NSStringFromClass($0) }, forKey: "navStack")
}
}
}Koden visar den kritiska hanteringen av Suspended i iOS. applicationDidEnterBackground — det sista garanterade anropet. All datasparning bör ske här: användartillstånd, navigeringsstack, utkast, timer. applicationWillEnterForeground anropas vid återkomst från Suspended eller Background. didFinishLaunchingWithOptions — endast vid cold start, när appen har laddats ur minnet efter Suspended.
I Android finns ingen direkt analog till iOS Suspended. Android fryser inte appar i minnet med bevarande av körkontext. Istället håller Android antingen processen i bakgrunden (Background) eller avslutar den (Not Running). Dock har en mekanism som heter App Freezer dykt upp på Android 11+ (API 30), som stoppar körningen av bakgrundsprocesser med SIGSTOP-signalen. Detta är en funktionell analog till Suspended, men med viktiga skillnader.
App Freezer — en del av Android-minneshanteringssystemet. När en app länge befinner sig i bakgrunden och inte har några aktiva notiser, skickar systemet SIGSTOP till den, vilket stoppar alla trådar. När appen återvänder till förgrunden skickas SIGCONT och körningen återupptas. Viktig skillnad från iOS: App Freezer garanterar inte bevarande av tillståndet — data i minnet kan gå förlorad om processen dödas under frysningen.
På Android rekommenderas användning av SavedStateHandle i ViewModel för automatisk sparande av tillstånd vid varje processavslutning. SavedStateHandle sparar data i Bundle via onSaveInstanceState, som överlever både App Freezer och Process Death. Till skillnad från iOS, där urladdning från Suspended är en exceptionell situation, är Process Death på Android normalt beteende som alltid bör förväntas.
// SavedStateHandle — räddning från Process Death på Android
class CheckoutViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// Tillstånd som överlever processen även efter 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
}
}
// Spara i onStop för App Freezer-fall
class MainActivity : AppCompatActivity() {
override fun onStop() {
super.onStop()
// Sparar data som måste överleva frysning
saveDraftData()
// Frigör resurser som inte behövs i fruset tillstånd
releaseHeavyResources()
// Varnar att appen kommer att frysas
// (Loggning för felsökning)
Log.d("Lifecycle", "Activity stoppad — möjlig App Freeze")
}
}Koden visar tillvägagångssättet för att hantera analogen till Suspended på Android. SavedStateHandle i ViewModel sparar och återställer automatiskt data vid Process Death. onStop — den sista garanterade händelsen före eventuell App Freezer eller processavslutning. Beställningsformulärets tillstånd, listan över produkter i varukorgen — all denna data överlever frysningen tack vare SavedStateHandle. För tunga resurser (bitmappar, databaskursorer) är onStop platsen för att frigöra minne.
State Restoration — den inbyggda iOS-mekanismen för att spara och återställa UI-tillståndet efter urladdning av appen från minnet. Om appen var i Suspended och systemet laddade ur den, återställer state restoration vid nästa cold start navigeringsstacken, scrollpositionen, formulärens tillstånd och andra UI-element. Användaren återvänder till samma skärm där han slutade.
State Restoration fungerar genom protokollen UIViewControllerRestoration och UIStateRestoring. Utvecklaren tilldelar restorationIdentifier till varje ViewController och View som ska återställas. När man går till Background kodar iOS tillståndet för dessa objekt. Efter återkomst från urladdning skapar iOS nya objekt och avkodar det sparade tillståndet. Utan state restoration kommer användaren att se en tom skärm efter cold start istället för platsen där han slutade.
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
// Återställer position efter dataladdning
}
}
}
// AppDelegate — aktivering av State Restoration
func application(
_ application: UIApplication,
shouldSaveSecureApplicationState coder: NSCoder
) -> Bool {
return true
}
func application(
_ application: UIApplication,
shouldRestoreSecureApplicationState coder: NSCoder
) -> Bool {
return true
}Koden visar implementeringen av State Restoration i iOS. restorationIdentifier och restorationClass är nödvändiga för varje ViewController som ska återställas. encodeRestorableState/decodeRestorableState sparar och laddar data via NSCoder. I AppDelegate aktiverar shouldSaveSecureApplicationState och shouldRestoreSecureApplicationState krypterad sparande av tillstånd. Från iOS 12+ rekommenderas användning av secure encoding (NSSecureCoding) för dataskydd.
Första regeln — räkna aldrig med att appen kommer tillbaka från Suspended. Systemet kan när som helst ladda ur appen. Alla kritiskt viktiga data måste sparas i permanent lagring före övergången till Suspended — det vill säga i applicationDidEnterBackground eller onStop. UserDefaults, Core Data, File Manager — lämpliga lagringsplatser. Minne (variabler, egenskaper) — en opålitlig lagringsplats för data som måste överleva Suspended.
Andra regeln — frigör resurser före Suspended. Stäng fildeskriptorer, frigör GPU-minne (Metal, Core Graphics), stäng nätverksanslutningar. Även om appen inte förbrukar CPU i Suspended, är upptagna resurser blockerade för andra appar. På iOS i Suspended kan man inte ha öppna sockets — vid återkomst från Suspended kan de vara obrukbara, vilket orsakar fel.
Tredje regeln — placera inte tidsberoende logik i väntan på återkomst från Suspended. Timer, callback-funktioner och nätverksaktivitet stoppas i Suspended. Om appen har varit i Suspended i flera timmar kan timern vid återkomst fungera felaktigt. Kontrollera datans aktualitet vid återkomst — kanske är cachen föråldrad och autentiseringstoken har upphört att gälla.
Fjärde regeln — använd State Restoration för alla skärmar, särskilt för inmatningsformulär, rullningsbara listor och detaljskärmar. Utan state restoration kommer användaren efter återkomst från urladdad Suspended att se appens startsida istället för platsen där han slutade. Detta försämrar användarupplevelsen och tvingar användaren att upprepa handlingar.
import UIKit
// Kontroll: har appen laddats ur från minnet?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Kontrollerar om det finns sparat tillstånd
if UserDefaults.standard.object(forKey: "navStack") != nil {
// Appen har laddats ur från Suspended
// Tillståndet måste återställas
restoreNavigationStack()
} else {
// Ren cold start från 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)
}
}
}Koden visar praxis för att avgöra om appen har laddats ur från Suspended. Kontroll av UserDefaults förekomst av sparad navigeringsstack gör det möjligt att skilja cold start efter urladdning från ren cold start. I det första fallet återställs navigeringsstacken, i det andra — visas onboarding eller huvudskärmen. Detta tillvägagångssätt kompletterar den inbyggda State Restoration för fall där NSCoder inte räcker till.
Vanliga frågor
Obegränsat — från några sekunder till flera dagar. iOS har ingen tidsgräns för Suspended. Appen kommer att finnas i minnet tills systemet beslutar att ladda ur den på grund av resursbrist. I praktiken förblir appar i Suspended från 15 minuter till flera timmar, beroende på enhetens RAM-minne och antalet aktiva appar.
Nej. applicationWillTerminate anropas inte när appen laddas ur från Suspended. Systemet frigör helt enkelt minnet utan att meddela appen. Detta är ytterligare en anledning till varför all datasparning bör ske i applicationDidEnterBackground. applicationWillTerminate anropas endast när användaren manuellt avslutar appen genom att svepa bort den från App Switcher.
Det finns ingen direkt analog. På Android 11+ har App Freezer dykt upp, som stoppar bakgrundsprocesser via SIGSTOP — funktionellt liknar det Suspended. Android-appar bör dock utformas med tanke på Process Death när som helst. Använd SavedStateHandle i ViewModel och onSaveInstanceState för att spara tillstånd som överlever både App Freezer och Process Death.
I iOS finns inget direkt API för kontroll. Indirekt metod: kontrollera UserDefaults förekomst av sparat tillstånd i didFinishLaunchingWithOptions. Om tillstånd finns — appen har laddats ur från Suspended och startar kallt. Om tillstånd inte finns — ren cold start. I SwiftUI kan du spara en flagga i scenePhase.background och kontrollera den vid nästa start.
Vid övergången till Suspended tar iOS en snapshot — en skärmbild av appens aktuella UI. Denna skärmbild visas i App Switcher och vid återkomst till appen (som en upptiningsanimation). Om appen innehåller konfidentiell data kan snapshot avslöja den. För skydd, använd UIApplication.shouldSnapshotSecureApp (iOS 16+) eller applicera en blur-overlay i applicationDidEnterBackground.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också