← Назад к вопросам

Расскажи про жизненный цикл iOS приложения

2.2 Middle🔥 131 комментариев
#Архитектура Flutter#ООП и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Жизненный цикл iOS приложения (App Lifecycle)

Понимание жизненного цикла iOS приложения критично для правильной работы с памятью, сохранением состояния и обработкой системных событий. Рассмотрю подробно все стадии.

Основные состояния приложения

┌─────────────┐
│   Not       │
│  Running    │ Приложение не запущено
└──────┬──────┘
       │ Пользователь тапает иконку
       ↓
┌──────────────────┐
│   Foreground     │ Активное приложение
│   (Running)      │ Видимо пользователю
└──────┬───────────┘
       │ Пользователь нажимает Home
       │ или уходит в другое приложение
       ↓
┌──────────────────┐
│   Background     │ Приложение в фоне
│   (Suspended)    │ Может выполнять задачи
└──────┬───────────┘
       │ Система закрывает приложение
       │ или пользователь свайпит его
       ↓
┌─────────────┐
│   Not       │
│  Running    │ Приложение удалено из памяти
└─────────────┘

Детальный цикл жизни

1. NOT RUNNING (Не запущено)
   └─ Приложение ещё не стартовало
   └─ Система загружает бинарник в память

2. FOREGROUND - INACTIVE (Неактивное, но видимое)
   └─ main() вызывается
   └─ didFinishLaunchingWithOptions вызывается
   └─ Приложение инициализируется
   └─ Происходит переходы: например сверхсписывание

3. FOREGROUND - ACTIVE (Активное)
   └─ applicationDidBecomeActive вызывается
   └─ Приложение полностью готово и работает
   └─ Пользователь может взаимодействовать

4. BACKGROUND - SUSPENDED (Фоновое)
   └─ applicationWillResignActive вызывается
   └─ applicationDidEnterBackground вызывается
   └─ ~5 секунд на завершение задач
   └─ После истечения → SUSPENDED

5. SUSPENDED (Приостановлено)
   └─ Приложение всё ещё в памяти
   └─ Не может выполнять код
   └─ Может быть возобновлено из этого состояния

6. NOT RUNNING - TERMINATED (Удалено)
   └─ applicationWillTerminate вызывается
   └─ Приложение удаляется из памяти
   └─ Максимум 10 секунд на сохранение данных

AppDelegate методы жизненного цикла

// iOS App Life Cycle Delegates

func application(
  _ application: UIApplication,
  didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
  // 1️⃣ САМЫЙ ПЕРВЫЙ метод
  // Вызывается когда приложение запускается
  
  // Здесь нужно:
  // - Инициализировать database
  // - Загрузить конфигурацию
  // - Установить root view controller
  // - Подписаться на push-уведомления
  // - Начать отслеживание location
  
  print("1. didFinishLaunchingWithOptions")
  return true
}

func applicationDidBecomeActive(
  _ application: UIApplication
) {
  // 2️⃣ Приложение стало активным и видимым
  // Вызывается когда пользователь видит экран
  
  // Здесь нужно:
  // - Возобновить отрисовку (resume rendering)
  // - Начать обновление UI (refresh data)
  // - Возобновить игру/анимацию
  // - Просить разрешения (permissions)
  
  print("2. applicationDidBecomeActive")
}

func applicationWillResignActive(
  _ application: UIApplication
) {
  // 3️⃣ Приложение теряет фокус
  // Вызывается когда:
  // - Пользователь нажимает Home
  // - Входящий звонок
  // - Приходит уведомление
  // - Открывается Control Center
  
  // Здесь нужно:
  // - Приостановить анимации
  // - Приостановить видео
  // - Приостановить звуки
  // - Сохранить временные данные
  
  print("3. applicationWillResignActive")
}

func applicationDidEnterBackground(
  _ application: UIApplication
) {
  // 4️⃣ Приложение вошло в фоновый режим
  // У приложения есть ~5 секунд на выполнение
  // После истечения→ SUSPENDED (заморожено)
  
  // Здесь нужно:
  // - Сохранить состояние (состояние экрана)
  // - Завершить активные задачи
  // - Сохранить данные на диск
  // - Отпустить дорогие ресурсы
  // - Закрыть сетевые соединения
  
  // ❌ НЕ делай:
  // - Долгие вычисления (будут прерваны)
  // - Sync с сервером (может потребовать время)
  
  print("4. applicationDidEnterBackground")
  
  // Опционально: запроси дополнительное время
  let bgTask = UIApplication.shared
    .beginBackgroundTask(expirationHandler: {
      // Время истекло
    })
  
  // Выполни быстрые задачи
  DispatchQueue.global().async {
    // Быстро сохрани данные
    UIApplication.shared.endBackgroundTask(bgTask)
  }
}

func applicationWillEnterForeground(
  _ application: UIApplication
) {
  // 5️⃣ Приложение возвращается из фона
  // Вызывается прямо перед applicationDidBecomeActive
  
  // Здесь нужно:
  // - Перезагрузить данные (может быть устаревшие)
  // - Обновить UI
  // - Повторно инициализировать соединения
  
  print("5. applicationWillEnterForeground")
}

func applicationWillTerminate(
  _ application: UIApplication
) {
  // 6️⃣ Приложение закрывается
  // У приложения есть ~10 секунд
  // После истечения → принудительное завершение
  
  // Здесь нужно:
  // - Финально сохранить состояние
  // - Закрыть базу данных
  // - Очистить временные файлы
  
  // ❌ НЕ полагайся на этот метод!
  // Часто не вызывается (система убивает процесс)
  
  print("6. applicationWillTerminate")
}

Схема вызовов

Пользователь тапает иконку приложения
    ↓
main()
    ↓
@UIApplicationMain
    ↓
Application:didFinishLaunchingWithOptions
    ↓
UIWindow создаётся
RootViewController устанавливается
    ↓
applicationDidBecomeActive
    ↓
✅ ПРИЛОЖЕНИЕ АКТИВНО
    ↓
    ┌─────────────────────────────────┐
    │  ПОЛЬЗОВАТЕЛЬ ВЗАИМОДЕЙСТВУЕТ    │
    │  - Тапает кнопки                 │
    │  - Скроллит списки               │
    │  - Вводит текст                  │
    └─────────────────────────────────┘
    ↓
Пользователь нажимает Home ↓ или приходит звонок ↓ или приходит SMS
    ↓
applicationWillResignActive
    ↓
applicationDidEnterBackground (~5 секунд)
    ↓
💤 SUSPENDED (Заморожено)
    ↓
Если пользователь вернулся в приложение:
    ↓
applicationWillEnterForeground
    ↓
applicationDidBecomeActive
    ↓
✅ ПРИЛОЖЕНИЕ АКТИВНО
    ↓
Если пользователь свайпит приложение:
    ↓
applicationWillTerminate (может не вызваться!)
    ↓
❌ NOT RUNNING

Пример: Сохранение состояния

class AppDelegate: UIResponder, UIApplicationDelegate {
  let userDefaults = UserDefaults.standard
  
  func applicationDidEnterBackground(_ application: UIApplication) {
    // Сохраняем текущий экран
    if let currentViewController = window?.rootViewController {
      userDefaults.set("HomeViewController", forKey: "lastScreen")
    }
    
    // Сохраняем данные
    let appState = AppState()
    let encoder = JSONEncoder()
    let data = try? encoder.encode(appState)
    userDefaults.set(data, forKey: "appState")
  }
  
  func applicationWillEnterForeground(_ application: UIApplication) {
    // Восстанавливаем состояние
    let decoder = JSONDecoder()
    if let data = userDefaults.data(forKey: "appState"),
       let appState = try? decoder.decode(AppState.self, from: data) {
      // Восстанавливаем UI
      restoreAppState(appState)
    }
  }
}

Background Tasks

// Выполнение задач в фоне (до 30 минут)
func applicationDidEnterBackground(_ application: UIApplication) {
  var bgTask = UIBackgroundTaskIdentifier.invalid
  
  bgTask = application.beginBackgroundTask(expirationHandler: {
    application.endBackgroundTask(bgTask)
    bgTask = UIBackgroundTaskIdentifier.invalid
  })
  
  DispatchQueue.global().async {
    // Выполняй задачи
    self.syncDataWithServer()
    self.downloadImages()
    
    // Не забудь завершить
    application.endBackgroundTask(bgTask)
  }
}

Как это влияет на Flutter?

// Flutter приложение работает на iOS и использует AppDelegate
// Но Flutter предоставляет свои методы для жизненного цикла

import 'package:flutter/services.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with WidgetsBindingObserver {  // Слушаем жизненный цикл
  
  @override
  void initState() {
    super.initState();
    // Когда виджет инициализируется
    WidgetsBinding.instance.addObserver(this);
  }
  
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    // Реагируем на изменения
    switch (state) {
      case AppLifecycleState.resumed:
        // applicationDidBecomeActive
        print('App resumed (активно)');
        break;
      case AppLifecycleState.paused:
        // applicationWillResignActive
        print('App paused (неактивно)');
        break;
      case AppLifecycleState.detached:
        // applicationWillTerminate
        print('App detached (закрыто)');
        break;
      case AppLifecycleState.hidden:
        // applicationDidEnterBackground (iOS 13+)
        print('App hidden (в фоне)');
        break;
    }
  }
  
  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: Text('Life Cycle Demo')),
    body: Center(
      child: Text('Watch console for lifecycle events'),
    ),
  );
}

Оптимизация для жизненного цикла

class OptimizedAppState extends State<MyApp>
    with WidgetsBindingObserver {
  
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed:
        // ✅ Возобнови:
        // - Стриминг видео
        // - Анимации
        // - Обновление location
        // - Обновление данных
        resumeStreaming();
        break;
        
      case AppLifecycleState.paused:
        // ✅ Приостанови:
        // - Видео
        // - Анимации
        // - Location tracking
        pauseStreaming();
        break;
        
      case AppLifecycleState.hidden:
        // ✅ Фоновые работы:
        // - Сохрани состояние
        // - Закрой соединения
        // - Освободи память
        saveState();
        break;
        
      case AppLifecycleState.detached:
        // ✅ Финальная очистка
        cleanup();
        break;
    }
  }
  
  void resumeStreaming() {
    // Возобнови фоновые задачи
  }
  
  void pauseStreaming() {
    // Остановки фоновые задачи
  }
  
  void saveState() {
    // Сохрани состояние приложения
  }
  
  void cleanup() {
    // Финальная очистка
  }
}

Резюме

Жизненный цикл iOS приложения:

  1. Not Running — приложение не запущено
  2. Foreground Inactive — запускается
  3. Foreground Active — активное, пользователь видит
  4. Background — ~5 секунд для задач
  5. Suspended — приостановлено в памяти
  6. Terminated — удалено

Основные методы:

  • didFinishLaunchingWithOptions — инициализация
  • applicationDidBecomeActive — становится активным
  • applicationWillResignActive — теряет фокус
  • applicationDidEnterBackground — фоновый режим
  • applicationWillTerminate — закрытие

Best Practice:

  • ✅ Сохраняй состояние в didEnterBackground
  • ✅ Возобнови данные в willEnterForeground
  • ✅ Приостанавливай ресурсоёмкие задачи
  • ✅ Не полагайся на applicationWillTerminate
Расскажи про жизненный цикл iOS приложения | PrepBro