Suspended — um estado pausado do ciclo de vida do aplicativo iOS no qual o app está congelado na memória mas não executa código. Mostramos como Suspended funciona, quais riscos o congelamento do app em segundo plano traz, como o iOS gerencia a descarga de aplicativos Suspended e como implementar state restoration para recuperação perfeita após retornar de Suspended.
Principais pontos
Suspended é um estado do ciclo de vida do aplicativo iOS no qual o app reside na RAM do dispositivo mas não executa nenhum código. Este é o estado final antes da terminação completa: o app transita de Background para Suspended após concluir todas as tarefas em segundo plano ou após expirar um tempo limite. No Suspended, o aplicativo está completamente congelado — todas as threads estão pausadas, os temporizadores não funcionam e não há atividade de rede.
Suspended é uma característica única do iOS, ausente no ciclo de vida padrão do Android. A razão está nas diferentes arquiteturas de gerenciamento de processos. O iOS preserva a imagem do aplicativo na memória (semelhante à hibernação de desktop) para que quando o usuário retornar, a interface possa ser instantaneamente restaurada sem uma inicialização a frio. O Android não tem Suspended — um processo existe e pode executar código (Background) ou está terminado (Not Running), embora o Android possa pausar a execução de threads via LMK.
Para o usuário, Suspended parece uma restauração instantânea: eles alternam entre aplicativos usando o App Switcher, e cada app abre exatamente onde eles pararam. Isso cria a ilusão de que todos os aplicativos estão rodando simultaneamente. Na realidade, a maioria deles está congelada em Suspended. Uma inicialização a quente de Suspended é muitas vezes mais rápida que uma inicialização a frio de Not Running, porque o código já está carregado na memória.
O iOS monitora o estado de todos os aplicativos e decide descarregar apps Suspended com base na memória disponível. Quando a memória está baixa, o sistema começa a descarregar apps Suspended, começando pelos que estão há mais tempo nesse estado. Se a memória ainda for insuficiente, o sistema transiciona apps de Background e Inactive para Suspended com descarga subsequente. Este processo é completamente transparente para o usuário — eles simplesmente veem o ícone do app no App Switcher, que ao ser tocado inicia uma inicialização a frio.
| Característica | Suspended (iOS) | Background (iOS) | Background (Android) |
|---|---|---|---|
| Executa código | Não | Sim (limitado) | Sim (limitado) |
| Na memória | Sim | Sim | Sim |
| Consumo de CPU | 0% | Baixo | Baixo |
| Inicialização a quente | Sim — restauração instantânea | Sim — via Inactive | Não — o processo pode ter sido morto |
| Tempo limite | Não — pode ficar na memória por horas | ~30 segundos (após beginBackgroundTask) | Depende da versão da API |
| Descarga pelo sistema | Quando a memória está baixa | Quando criticamente baixa | LMK (Low Memory Killer) |
| Retorno ao trabalho | Do App Switcher — instantaneamente | Do App Switcher — via Inactive | Inicialização a frio |
| State Restoration | Recomendado | Não necessário | SavedStateHandle |
No iOS, Suspended é alcançado automaticamente após a conclusão de todas as tarefas em segundo plano. O sistema chama applicationDidEnterBackground, dá tempo para executar beginBackgroundTask (cerca de 30 segundos), então pausa forçadamente todas as threads e transiciona o app para Suspended. Os objetos na memória são preservados, mas nenhum código é executado — o aplicativo está congelado em seu estado atual.
Um ponto criticamente importante: applicationDidEnterBackground é o último método garantido a ser chamado antes de Suspended. Depois disso, o app não recebe nenhuma notificação sobre descarga de memória. Se o usuário ou o sistema matar o app enquanto ele está em Suspended, nem applicationWillTerminate nem applicationDidEnterBackground são chamados novamente. Portanto, todo salvamento de dados deve acontecer em applicationDidEnterBackground, não em applicationWillTerminate.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// Última chamada garantida antes de Suspended
func applicationDidEnterBackground(_ application: UIApplication) {
// Salvar tudo que precisa sobreviver à descarga de memória
savePersistentState()
saveNavigationStack()
// Solicitar tempo extra se necessário
let task = application.beginBackgroundTask {
application.endBackgroundTask(task)
}
}
// Retorno de Suspended — inicialização a quente
func applicationWillEnterForeground(_ application: UIApplication) {
// App estava em Suspended, retomando trabalho
print("Retorno de Suspended ou Background")
}
// Restauração completa após descarga de memória
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Se esta é uma inicialização a frio após descarga de Suspended —
// restaurar state restoration
return true
}
private func savePersistentState() {
UserDefaults.standard.set(Date(), forKey: "lastActiveDate")
}
private func saveNavigationStack() {
guard let rootVC = window?.rootViewController else { return }
// Salvar a pilha de navegação atual
if let navController = rootVC as? UINavigationController {
let vcClasses = navController.viewControllers.map { type(of: $0) }
UserDefaults.standard.set(vcClasses.map { NSStringFromClass($0) }, forKey: "navStack")
}
}
}O código mostra o manuseio criticamente importante de Suspended no iOS. applicationDidEnterBackground é a última chamada garantida. Todo salvamento de dados deve acontecer aqui: estado do usuário, pilha de navegação, rascunhos, temporizadores. applicationWillEnterForeground é chamado ao retornar de Suspended ou Background. didFinishLaunchingWithOptions — apenas na inicialização a frio, quando o app foi descarregado da memória após Suspended.
No Android, não existe um análogo direto do Suspended do iOS. O Android não congela aplicativos na memória preservando o contexto de execução. Em vez disso, o Android mantém o processo em segundo plano ou o termina. No entanto, no Android 11+ (API 30), foi introduzido um mecanismo chamado App Freezer, que suspende a execução de processos em segundo plano usando o sinal SIGSTOP. Isso é funcionalmente semelhante ao Suspended, mas com diferenças importantes.
App Freezer faz parte do sistema de gerenciamento de memória do Android. Quando um app fica muito tempo em segundo plano sem notificações ativas, o sistema envia SIGSTOP, pausando todas as threads. Quando o app retorna ao primeiro plano, SIGCONT é enviado e a execução é retomada. A principal diferença do iOS: o App Freezer não garante a preservação do estado — os dados na memória podem ser perdidos se o processo for morto durante o congelamento.
No Android, é recomendado usar SavedStateHandle no ViewModel para preservação automática do estado durante qualquer terminação de processo. O SavedStateHandle salva dados em um Bundle via onSaveInstanceState, que sobrevive tanto ao App Freezer quanto ao Process Death. Diferente do iOS, onde a descarga de Suspended é uma situação excepcional, no Android o Process Death é um comportamento normal que deve ser sempre esperado.
// SavedStateHandle — salvação do Process Death no Android
class CheckoutViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// Estado que sobrevive ao processo mesmo após 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
}
}
// Salvar em onStop em caso de App Freezer
class MainActivity : AppCompatActivity() {
override fun onStop() {
super.onStop()
// Salvar dados que devem sobreviver ao congelamento
saveDraftData()
// Liberar recursos não necessários em estado congelado
releaseHeavyResources()
// Avisar que o app será congelado
// (Registro para depuração)
Log.d("Lifecycle", "Activity stopped — possível App Freeze")
}
}O código mostra a abordagem para lidar com o análogo do Suspended no Android. SavedStateHandle no ViewModel salva e restaura dados automaticamente durante o Process Death. onStop é o último evento garantido antes de um possível App Freezer ou terminação do processo. O estado do formulário de checkout, a lista de itens no carrinho — todos esses dados sobrevivem ao congelamento graças ao SavedStateHandle. Para recursos pesados (bitmaps, cursores de BD), onStop é o lugar para liberar memória.
State Restoration é um mecanismo incorporado do iOS para salvar e restaurar o estado da UI após o app ser descarregado da memória. Se o app estava em Suspended e o sistema o descarregou, na próxima inicialização a frio o state restoration restaura a pilha de navegação, a posição de rolagem, o estado do formulário e outros elementos da UI. O usuário retorna à mesma tela onde estava.
O State Restoration funciona através dos protocolos UIViewControllerRestoration e UIStateRestoring. O desenvolvedor atribui um restorationIdentifier a cada ViewController e View que deseja restaurar. Ao ir para segundo plano, o iOS codifica o estado desses objetos. Ao retornar após uma descarga, o iOS cria novos objetos e decodifica o estado salvo. Sem state restoration, o usuário verá uma tela em branco após uma inicialização a frio em vez de onde parou.
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
// Restaurar posição após carregamento de dados
}
}
}
// AppDelegate — ativando State Restoration
func application(
_ application: UIApplication,
shouldSaveSecureApplicationState coder: NSCoder
) -> Bool {
return true
}
func application(
_ application: UIApplication,
shouldRestoreSecureApplicationState coder: NSCoder
) -> Bool {
return true
}O código mostra a implementação do State Restoration no iOS. restorationIdentifier e restorationClass são necessários para cada ViewController restaurável. encodeRestorableState/decodeRestorableState salvam e carregam dados via NSCoder. No AppDelegate, shouldSaveSecureApplicationState e shouldRestoreSecureApplicationState habilitam a preservação criptografada do estado. No iOS 12+, é recomendado usar codificação segura (NSSecureCoding) para proteção de dados.
A primeira regra — nunca presuma que o app retornará de Suspended. O sistema pode descarregar o app a qualquer momento. Todos os dados criticamente importantes devem ser salvos em armazenamento persistente antes da transição para Suspended — ou seja, em applicationDidEnterBackground ou onStop. UserDefaults, Core Data, File Manager — opções de armazenamento adequadas. A memória (variáveis, propriedades) é um armazenamento não confiável para dados que devem sobreviver ao Suspended.
A segunda regra — libere recursos antes de Suspended. Feche descritores de arquivo, libere memória GPU (Metal, Core Graphics), feche conexões de rede. Embora o app não consuma CPU no Suspended, os recursos ocupados ficam bloqueados para outros aplicativos. No iOS, não se pode manter sockets abertos em Suspended — ao retornar de Suspended, eles podem não estar funcionais, causando erros.
A terceira regra — não coloque lógica dependente de tempo esperando retornar de Suspended. Temporizadores, callbacks e atividade de rede cessam em Suspended. Se o app ficou em Suspended por várias horas, um temporizador pode disparar incorretamente ao retornar. Verifique a validade dos dados ao retornar — o cache pode estar obsoleto e o token de autorização pode ter expirado.
A quarta regra — use State Restoration para todas as telas, especialmente formulários de entrada, listas com rolagem e telas de detalhes. Sem state restoration, após retornar de um Suspended descarregado, o usuário verá a tela inicial do app em vez de onde parou. Isso degrada a experiência do usuário e força o usuário a repetir ações.
import UIKit
// Verificação: o app foi descarregado da memória?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Verificar se existe estado salvo
if UserDefaults.standard.object(forKey: "navStack") != nil {
// O app foi descarregado de Suspended
// Precisa restaurar o estado
restoreNavigationStack()
} else {
// Inicialização a frio limpa de 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)
}
}
}O código mostra a prática de determinar se o app foi descarregado de Suspended. Verificar UserDefaults quanto à presença de uma pilha de navegação salva permite distinguir uma inicialização a frio após descarga de uma inicialização a frio limpa. No primeiro caso, a pilha de navegação é restaurada; no segundo, a tela de onboarding ou principal é mostrada. Esta abordagem complementa o State Restoration incorporado para casos onde o NSCoder é insuficiente.
Perguntas frequentes
Ilimitado — de alguns segundos a vários dias. O iOS não tem tempo limite para Suspended. O app permanecerá na memória até que o sistema decida descarregá-lo por falta de recursos. Na prática, os apps ficam em Suspended de 15 minutos a várias horas, dependendo da RAM do dispositivo e do número de aplicativos ativos.
Não. O applicationWillTerminate não é chamado quando o app é descarregado de Suspended. O sistema simplesmente libera memória sem notificar o aplicativo. Esta é mais uma razão pela qual todo salvamento de dados deve ocorrer em applicationDidEnterBackground. O applicationWillTerminate só é chamado quando o usuário termina manualmente o app deslizando-o para fora do App Switcher.
Não existe um análogo direto. No Android 11+, foi introduzido o App Freezer, que pausa processos em segundo plano via SIGSTOP — isso é funcionalmente semelhante ao Suspended. No entanto, os apps Android devem ser projetados assumindo Process Death a qualquer momento. Use SavedStateHandle no ViewModel e onSaveInstanceState para salvar o estado que sobreviverá tanto ao App Freezer quanto ao Process Death.
No iOS, não há uma API direta para verificar. Um método indireto: verificar UserDefaults quanto à presença de estado salvo em didFinishLaunchingWithOptions. Se o estado existir — o app foi descarregado de Suspended e está iniciando a frio. Se o estado não existir — é uma inicialização a frio limpa. No SwiftUI, pode-se salvar uma flag em scenePhase.background e verificá-la no próximo lançamento.
Ao fazer a transição para Suspended, o iOS tira um snapshot — uma captura de tela da UI atual do app. Esta captura é mostrada no App Switcher e ao retornar ao app (como uma animação de “descongelamento”). Se o app contiver dados confidenciais, o snapshot pode expô-los. Para proteção, use UIApplication.shouldSnapshotSecureApp (iOS 16+) ou aplique uma sobreposição de desfoque em applicationDidEnterBackground.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também