Suspended — ما هو، تجميد التطبيق في الخلفية في iOS

المؤلف: IT Sectr نُشر: 2026-03-03 وقت القراءة: 11 دق

Suspended — حالة موقفة من دورة حياة تطبيق iOS يتم فيها تجميد التطبيق في الذاكرة ولكن لا ينفذ أي كود. نعرض كيف يعمل Suspended، وما المخاطر التي يحملها تجميد التطبيق في الخلفية، وكيف يدير iOS تفريغ تطبيقات Suspended، وكيفية تنفيذ state restoration للاسترداد السلس بعد العودة من Suspended.

الرئيسية

  • Suspended — التطبيق مجمد في الذاكرة، لا يتم تنفيذ أي كود، يتم الحفاظ على مكدس الواجهة
  • iOS Suspended — حالة فريدة غير موجودة في Android؛ العملية موجودة ولكنها غير نشطة
  • التفريغ — عند نقص الذاكرة، يتم إزالة تطبيقات Suspended أولاً، وتفقد البيانات في الذاكرة
  • State Restoration — آلية iOS لحفظ واستعادة مكدس الواجهة بعد التفريغ
  • didEnterBackground — آخر طريقة مضمونة الاستدعاء قبل Suspended

Suspended — ما هذه الحالة

Suspended هي حالة من دورة حياة تطبيق iOS يكون فيها التطبيق موجوداً في ذاكرة الوصول العشوائي للجهاز ولكن لا ينفذ أي كود. هذه هي الحالة النهائية قبل الإنهاء الكامل: ينتقل التطبيق إلى Suspended من Background بعد إكمال جميع المهام الخلفية أو بعد انتهاء المهلة. في Suspended، التطبيق مجمد تماماً — جميع الخيوط موقفة، المؤقتات لا تعمل، ولا يوجد نشاط شبكي.

Suspended هي ميزة فريدة لنظام iOS، غير موجودة في دورة حياة Android القياسية. السبب يكمن في اختلاف بنية إدارة العمليات. يحتفظ iOS بصورة التطبيق في الذاكرة (مشابه للإسبات على سطح المكتب) ليتمكن عند عودة المستخدم من استعادة الواجهة فوراً دون بدء بارد. Android لا يحتوي على Suspended — العملية إما موجودة ويمكنها تنفيذ كود (Background) أو منتهية (Not Running)، على الرغم من أن Android يمكنه إيقاف تنفيذ الخيوط عبر LMK.

بالنسبة للمستخدم، يبدو Suspended كاستعادة فورية: ينتقل بين التطبيقات باستخدام App Switcher، وكل تطبيق يفتح من حيث تركه. هذا يخلق وهم أن جميع التطبيقات تعمل في وقت واحد. في الواقع، معظمها مجمدة في Suspended. البدء الساخن من Suspended أسرع بعدة مرات من البدء البارد من Not Running، لأن الكود محمل بالفعل في الذاكرة.

كيف يدير النظام Suspended

يتتبع iOS حالة جميع التطبيقات ويقرر تفريغ تطبيقات Suspended بناءً على الذاكرة المتاحة. عندما تكون الذاكرة منخفضة، يبدأ النظام في تفريغ تطبيقات Suspended، بدءاً من تلك التي بقيت أطول في هذه الحالة. إذا كانت الذاكرة لا تزال غير كافية، ينقل النظام التطبيقات من Background و Inactive إلى Suspended مع التفريغ اللاحق. هذه العملية شفافة تماماً للمستخدم — يرون ببساطة أيقونة التطبيق في App Switcher، والتي تبدأ بدءاً بارداً عند النقر عليها.

الخاصيةSuspended (iOS)Background (iOS)Background (Android)
تنفيذ الكودلانعم (محدود)نعم (محدود)
في الذاكرةنعمنعمنعم
استهلاك المعالج0%منخفضمنخفض
البدء الساخننعم — استعادة فوريةنعم — عبر Inactiveلا — قد تكون العملية قد قتلت
المهلةلا — يمكن البقاء في الذاكرة لساعات~30 ثانية (بعد beginBackgroundTask)يعتمد على إصدار API
التفريغ من النظامعند نقص الذاكرةعند النقص الحاد في الذاكرةLMK (Low Memory Killer)
العودة للعملمن App Switcher — فوراًمن App Switcher — عبر Inactiveبدء بارد
State Restorationموصى بهغير مطلوبSavedStateHandle

Suspended في iOS: آلية التجميد

في iOS، يتم الوصول إلى Suspended تلقائياً بعد إكمال جميع المهام الخلفية. يستدعي النظام applicationDidEnterBackground، ويعطي وقتاً لتنفيذ beginBackgroundTask (حوالي 30 ثانية)، ثم يوقف جميع الخيوط قسراً وينقل التطبيق إلى Suspended. يتم الحفاظ على الكائنات في الذاكرة، ولكن لا يتم تنفيذ أي كود — التطبيق مجمد في حالته الحالية.

نقطة بالغة الأهمية: applicationDidEnterBackground هو آخر طريقة مضمونة الاستدعاء قبل Suspended. بعد ذلك، لا يتلقى التطبيق أي إشعارات حول تفريغ الذاكرة. إذا قام المستخدم أو النظام بقتل التطبيق وهو في Suspended، لا يتم استدعاء applicationWillTerminate ولا applicationDidEnterBackground مرة أخرى. لذلك، يجب أن يتم حفظ جميع البيانات في applicationDidEnterBackground، وليس في applicationWillTerminate.

swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    // آخر استدعاء مضمون قبل Suspended
    func applicationDidEnterBackground(_ application: UIApplication) {
        // حفظ كل ما يحتاج للنجاة من تفريغ الذاكرة
        savePersistentState()
        saveNavigationStack()

        // طلب وقت إضافي إذا لزم الأمر
        let task = application.beginBackgroundTask {
            application.endBackgroundTask(task)
        }
    }

    // العودة من Suspended — بدء ساخن
    func applicationWillEnterForeground(_ application: UIApplication) {
        // كان التطبيق في Suspended، استئناف العمل
        print("العودة من Suspended أو Background")
    }

    // الاستعادة الكاملة بعد تفريغ الذاكرة
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // إذا كان هذا بدءاً بارداً بعد تفريغ من Suspended —
        // استعادة state restoration
        return true
    }

    private func savePersistentState() {
        UserDefaults.standard.set(Date(), forKey: "lastActiveDate")
    }

    private func saveNavigationStack() {
        guard let rootVC = window?.rootViewController else { return }
        // حفظ مكدس التنقل الحالي
        if let navController = rootVC as? UINavigationController {
            let vcClasses = navController.viewControllers.map { type(of: $0) }
            UserDefaults.standard.set(vcClasses.map { NSStringFromClass($0) }, forKey: "navStack")
        }
    }
}

يظهر الكود المعالجة البالغة الأهمية لـ Suspended على iOS. applicationDidEnterBackground هو آخر استدعاء مضمون. يجب أن يتم حفظ جميع البيانات هنا: حالة المستخدم، مكدس التنقل، المسودات، المؤقتات. يتم استدعاء applicationWillEnterForeground عند العودة من Suspended أو Background. didFinishLaunchingWithOptions — فقط عند البدء البارد، عندما تم تفريغ التطبيق من الذاكرة بعد Suspended.

Suspended في Android — هل يوجد نظير

في Android، لا يوجد نظير مباشر لـ Suspended في iOS. لا يقوم Android بتجميد التطبيقات في الذاكرة مع الحفاظ على سياق التنفيذ. بدلاً من ذلك، إما أن يبقي Android العملية في الخلفية أو ينهيها. ومع ذلك، في Android 11+ (API 30) تم تقديم آلية تسمى App Freezer، والتي توقف تنفيذ العمليات الخلفية باستخدام إشارة SIGSTOP. هذا مشابه وظيفياً لـ Suspended، ولكن مع اختلافات مهمة.

App Freezer هو جزء من نظام إدارة الذاكرة في Android. عندما يكون التطبيق في الخلفية لفترة طويلة دون إشعارات نشطة، يرسل له النظام SIGSTOP، مما يوقف جميع الخيوط. عندما يعود التطبيق إلى المقدمة، يتم إرسال SIGCONT وتستأنف التنفيذ. الفرق الرئيسي عن iOS: App Freezer لا يضمن الحفاظ على الحالة — قد تُفقد البيانات في الذاكرة إذا تم قتل العملية أثناء التجميد.

في Android، يوصى باستخدام SavedStateHandle في ViewModel للحفظ التلقائي للحالة أثناء أي إنهاء للعملية. يحفظ SavedStateHandle البيانات في Bundle عبر onSaveInstanceState، الذي ينجو من كل من App Freezer و Process Death. على عكس iOS، حيث التفريغ من Suspended هو حالة استثنائية، في Android Process Death هو سلوك طبيعي يجب توقعه دائماً.

kotlin
// SavedStateHandle — خلاص من Process Death على Android
class CheckoutViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    // حالة تنجو من العملية حتى بعد 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
    }
}

// الحفظ في onStop في حالة App Freezer
class MainActivity : AppCompatActivity() {

    override fun onStop() {
        super.onStop()
        // حفظ البيانات التي يجب أن تنجو من التجميد
        saveDraftData()
        // تحرير الموارد غير الضرورية في الحالة المجمدة
        releaseHeavyResources()
        // تحذير من أن التطبيق سيتجمد
        // (تسجيل للتصحيح)
        Log.d("Lifecycle", "Activity stopped — احتمال App Freeze")
    }
}

يظهر الكود نهج معالجة نظير Suspended على Android. SavedStateHandle في ViewModel يحفظ ويستعيد البيانات تلقائياً أثناء Process Death. onStop هو آخر حدث مضمون قبل App Freezer محتمل أو إنهاء العملية. حالة نموذج الدفع، قائمة العناصر في السلة — كل هذه البيانات تنجو من التجميد بفضل SavedStateHandle. للموارد الثقيلة (الرسومات النقطية، مؤشرات قاعدة البيانات)، onStop هو المكان لتحرير الذاكرة.

State Restoration: الاسترداد بعد Suspended

State Restoration هي آلية مدمجة في iOS لحفظ واستعادة حالة الواجهة بعد تفريغ التطبيق من الذاكرة. إذا كان التطبيق في Suspended وقام النظام بتفريغه، في البدء البارد التالي يستعيد state restoration مكدس التنقل وموضع التمرير وحالة النماذج وعناصر الواجهة الأخرى. يعود المستخدم إلى نفس الشاشة حيث توقف.

يعمل State Restoration من خلال بروتوكولات UIViewControllerRestoration و UIStateRestoring. يقوم المطور بتعيين restorationIdentifier لكل ViewController و View يريد استعادتها. عند الذهاب إلى الخلفية، يقوم iOS بتشفير حالة هذه الكائنات. عند العودة بعد التفريغ، يقوم iOS بإنشاء كائنات جديدة وفك تشفير الحالة المحفوظة. بدون state restoration، سيرى المستخدم شاشة فارغة بعد البدء البارد بدلاً من حيث توقف.

swift
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
            // استعادة الموضع بعد تحميل البيانات
        }
    }
}

// AppDelegate — تفعيل State Restoration
func application(
    _ application: UIApplication,
    shouldSaveSecureApplicationState coder: NSCoder
) -> Bool {
    return true
}

func application(
    _ application: UIApplication,
    shouldRestoreSecureApplicationState coder: NSCoder
) -> Bool {
    return true
}

يظهر الكود تنفيذ State Restoration على iOS. restorationIdentifier و restorationClass ضروريان لكل ViewController قابل للاستعادة. encodeRestorableState/decodeRestorableState يحفظان ويحملان البيانات عبر NSCoder. في AppDelegate، shouldSaveSecureApplicationState و shouldRestoreSecureApplicationState يفعلان الحفظ المشفر للحالة. في iOS 12+، يوصى باستخدام التشفير الآمن (NSSecureCoding) لحماية البيانات.

أفضل الممارسات للعمل مع Suspended

القاعدة الأولى — لا تفترض أبداً أن التطبيق سيعود من Suspended. يمكن للنظام تفريغ التطبيق في أي لحظة. يجب حفظ جميع البيانات بالغة الأهمية في تخزين دائم قبل الانتقال إلى Suspended — أي في applicationDidEnterBackground أو onStop. UserDefaults, Core Data, File Manager — خيارات تخزين مناسبة. الذاكرة (المتغيرات، الخصائص) هي تخزين غير موثوق للبيانات التي يجب أن تنجو من Suspended.

القاعدة الثانية — حرر الموارد قبل Suspended. أغلق واصفات الملفات، حرر ذاكرة GPU (Metal, Core Graphics)، أغلق اتصالات الشبكة. على الرغم من أن التطبيق لا يستهلك وحدة المعالجة المركزية في Suspended، فإن الموارد المشغولة محجوبة عن التطبيقات الأخرى. في iOS، لا يمكن الاحتفاظ بمآخذ مفتوحة في Suspended — عند العودة من Suspended، قد لا تكون عاملة، مما يسبب أخطاء.

القاعدة الثالثة — لا تضع منطقاً يعتمد على الوقت منتظراً العودة من Suspended. تتوقف المؤقتات والاستدعاءات والنشاط الشبكي في Suspended. إذا كان التطبيق في Suspended لعدة ساعات، قد يعمل المؤقت بشكل غير صحيح عند العودة. تحقق من صحة البيانات عند العودة — قد تكون ذاكرة التخزين المؤقت قديمة، وقد يكون رمز التفويض قد انتهى.

القاعدة الرابعة — استخدم State Restoration لجميع الشاشات، خاصة نماذج الإدخال والقوائم القابلة للتمرير وشاشات التفاصيل. بدون state restoration، بعد العودة من Suspended المفرغ، سيرى المستخدم الشاشة الأولية للتطبيق بدلاً من حيث توقف. هذا يضعف تجربة المستخدم ويجبر المستخدم على تكرار الإجراءات.

swift
import UIKit

// التحقق: هل تم تفريغ التطبيق من الذاكرة؟
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    // التحقق من وجود حالة محفوظة
    if UserDefaults.standard.object(forKey: "navStack") != nil {
        // تم تفريغ التطبيق من Suspended
        // يحتاج لاستعادة الحالة
        restoreNavigationStack()
    } else {
        // بدء بارد نظيف من 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)
        }
    }
}

يظهر الكود ممارسة تحديد ما إذا كان التطبيق قد تم تفريغه من Suspended. التحقق من UserDefaults لوجود مكدس تنقل محفوظ يسمح بتمييز البدء البارد بعد التفريغ من البدء البارد النظيف. في الحالة الأولى، يتم استعادة مكدس التنقل؛ في الثانية، يتم عرض شاشة الترحيب أو الرئيسية. هذا النهج يكمل State Restoration المدمج للحالات التي لا يكون فيها NSCoder كافياً.

الأسئلة المتكررة

كم من الوقت يمكن للتطبيق البقاء في Suspended؟

غير محدود — من بضع ثوان إلى عدة أيام. iOS ليس لديه مهلة لـ Suspended. سيبقى التطبيق في الذاكرة حتى يقرر النظام تفريغه بسبب نقص الموارد. في الممارسة، تبقى التطبيقات في Suspended من 15 دقيقة إلى عدة ساعات، حسب حجم ذاكرة الوصول العشوائي للجهاز وعدد التطبيقات النشطة.

هل يتم استدعاء applicationWillTerminate عند التفريغ من Suspended؟

لا. لا يتم استدعاء applicationWillTerminate عند تفريغ التطبيق من Suspended. يقوم النظام ببساطة بتحرير الذاكرة دون إخطار التطبيق. هذا سبب آخر لماذا يجب أن يتم حفظ جميع البيانات في applicationDidEnterBackground. يتم استدعاء applicationWillTerminate فقط عندما ينهي المستخدم التطبيق يدوياً عن طريق السحب من App Switcher.

هل لدى Android نظير لـ Suspended؟

لا يوجد نظير مباشر. في Android 11+، تم تقديم App Freezer، الذي يوقف العمليات الخلفية عبر SIGSTOP — هذا مشابه وظيفياً لـ Suspended. ومع ذلك، يجب تصميم تطبيقات Android مع افتراض Process Death في أي لحظة. استخدم SavedStateHandle في ViewModel و onSaveInstanceState لحفظ الحالة التي ستنجو من كل من App Freezer و Process Death.

كيف تتحقق مما إذا كان التطبيق في Suspended؟

في iOS، لا توجد API مباشرة للتحقق. طريقة غير مباشرة: التحقق من UserDefaults لوجود حالة محفوظة في didFinishLaunchingWithOptions. إذا كانت الحالة موجودة — تم تفريغ التطبيق من Suspended ويبدأ بارداً. إذا لم تكن الحالة موجودة — بدء بارد نظيف. في SwiftUI، يمكن حفظ علامة في scenePhase.background والتحقق منها عند الإطلاق التالي.

ما هي اللقطة (snapshot) في سياق Suspended؟

عند الانتقال إلى Suspended، يأخذ iOS لقطة — لقطة شاشة للواجهة الحالية للتطبيق. تظهر هذه اللقطة في App Switcher وعند العودة إلى التطبيق (كرسم متحرك “إذابة”). إذا كان التطبيق يحتوي على بيانات سرية، قد تكشفها اللقطة. للحماية، استخدم UIApplication.shouldSnapshotSecureApp (iOS 16+) أو طبق تراكب ضبابي في applicationDidEnterBackground.

الخلاصة

  • Suspended — التطبيق مجمد في ذاكرة iOS، لا يتم تنفيذ كود، ولكن يتم الحفاظ على مكدس الواجهة للاستعادة الفورية
  • تفرد iOS — Suspended غير موجود في Android؛ يستخدم Android App Freezer (SIGSTOP) كنظير جزئي
  • التفريغ — يقوم النظام بتفريغ تطبيقات Suspended كأولوية أولى عند نقص الذاكرة، دون إخطار
  • الحفظ — applicationDidEnterBackground هي آخر طريقة مضمونة؛ يجب حفظ جميع البيانات هنا
  • State Restoration — آلية قائمة على NSCoder للاستعادة التلقائية للواجهة بعد التفريغ من Suspended
  • البديل في Android — SavedStateHandle + onSaveInstanceState للنجاة من Process Death
  • اللقطة — يأخذ iOS لقطة شاشة أثناء Suspended؛ يجب إخفاء البيانات السرية عبر تراكب ضبابي أو لقطة آمنة

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا