الثبات في الإنشاء

تعتبر Compose الأنواع إما ثابتة أو غير ثابتة. يكون النوع ثابتًا إذا كان غير قابل للتغيير، أو إذا كان بإمكان Compose معرفة ما إذا كانت قيمته قد تغيّرت بين عمليات إعادة التركيب. يكون النوع غير ثابت إذا لم يتمكّن Compose من معرفة ما إذا كانت قيمته قد تغيّرت بين عمليات إعادة التركيب.

تستخدم Compose ثبات مَعلمات العنصر القابل للإنشاء لتحديد كيفية مقارنة المدخلات وتحديد ما إذا كان يمكن تخطّي العنصر القابل للإنشاء أثناء إعادة التركيب (مع تفعيل وضع التخطّي الصارم تلقائيًا بدءًا من Kotlin 2.0.20):

  • المَعلمات الثابتة: تقارن Compose المَعلمات الثابتة باستخدام التساوي البنيوي (Object.equals()). إذا كانت المَعلمات الثابتة لعنصر قابل للإنشاء تساوي قيمها السابقة، تتخطّى Compose هذا العنصر.
  • المَعلمات غير الثابتة: عند تفعيل وضع التخطّي القوي (وهو الوضع التلقائي في Kotlin 2.0.20 والإصدارات الأحدث)، يقارن Compose المَعلمات غير الثابتة باستخدام تساوي المثيل (===) ويتخطّى الدالة المركّبة إذا تم تمرير مثيلات الكائن نفسها. في الإصدارات الأقدم من Kotlin 2.0.20 (أو عند إيقاف وضع Strong skipping)، يعيد Compose دائمًا تركيب عنصر قابل للإنشاء يتضمّن مَعلمات غير ثابتة عندما يعيد العنصر الرئيسي تركيبه.

إذا كان تطبيقك يخصّص بشكل متكرّر مثيلات جديدة لمعلمات غير ثابتة، أو يفرض عمليات تحقّق .equals() مكلفة على مجموعات كبيرة من خلال إضافة تعليقات توضيحية مفرطة إلى النماذج، قد تلاحظ عمليات إعادة إنشاء غير ضرورية أو زيادة في تكلفة المقارنة.

يوضّح هذا المستند بالتفصيل كيف يحدّد Compose الثبات وكيف يمكنك تحسينه لتحسين الأداء وتجربة المستخدم بشكل عام.

العناصر غير القابلة للتغيير

توضّح المقتطفات التالية المبادئ العامة للاستقرار وإعادة التركيب.

الفئة Contact هي فئة بيانات غير قابلة للتغيير. ويرجع ذلك إلى أنّ جميع مَعلماتها هي عناصر أساسية محدّدة باستخدام الكلمة الرئيسية val. بعد إنشاء مثيل من Contact، لا يمكنك تغيير قيمة خصائص العنصر. وفي حال محاولة إجراء ذلك، سيتم إنشاء عنصر جديد.

data class Contact(val name: String, val number: String)

يحتوي العنصر القابل للإنشاء ContactRow على مَعلمة من النوع Contact.

@Composable
fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
   var selected by remember { mutableStateOf(false) }

   Row(modifier) {
      ContactDetails(contact)
      ToggleButton(selected, onToggled = { selected = !selected })
   }
}

ضع في اعتبارك ما يحدث عندما ينقر المستخدم على زر التبديل وتتغير الحالة selected:

  1. تُقيّم Compose ما إذا كان يجب إعادة إنشاء الرمز داخل ContactRow.
  2. يرى أنّ الوسيطة الوحيدة لـ ContactDetails هي من النوع Contact.
  3. بما أنّ Contact هي فئة بيانات غير قابلة للتغيير، يتأكّد Compose من عدم تغيير أي من وسيطات ContactDetails.
  4. نتيجةً لذلك، يتخطّى Compose ContactDetails ولا يعيد تركيبه.
  5. من ناحية أخرى، تغيّرت وسيطات ToggleButton، لذا يعيد Compose إنشاء هذا المكوّن.

الكائنات القابلة للتغيير

على الرغم من أنّ المثال السابق يستخدم كائنًا غير قابل للتغيير، من الممكن إنشاء كائن قابل للتغيير. ضَع في اعتبارك المقتطف التالي:

data class Contact(var name: String, var number: String)

بما أنّ كل مَعلمة من Contact أصبحت الآن var، لم يعُد الصف غير قابل للتغيير. إذا تغيّرت خصائصه، لن يتم إعلام Compose بذلك. ويرجع ذلك إلى أنّ Compose يتتبّع التغييرات التي تطرأ على عناصر الحالة في Compose فقط.

تعتبر Compose هذا النوع من الفئات غير ثابت. في وضع التخطّي القوي (الإصدار 2.0.20 من Kotlin أو الإصدارات الأحدث)، تقارن Compose Contact باستخدام تساوي المثيل (===): لن يؤدي تغيير contact.name في مكانه إلى إعادة التركيب، بينما سيؤدي تمرير مثيل Contact تم تخصيصه حديثًا بقيم متطابقة إلى فرض إعادة تركيب ContactDetails (وبدون وضع التخطّي القوي، تتم إعادة تركيب ContactDetails في كل مرة يتغير فيها selected).

التنفيذ في Compose

قد يكون من المفيد، ولكن ليس من الضروري، معرفة الطريقة التي يحدّد بها Compose الدوال التي سيتم تخطّيها أثناء إعادة التركيب.

عندما يتم تشغيل برنامج التجميع Compose على الرمز البرمجي، يضع علامة على كل دالة ونوع باستخدام إحدى العلامات المتعددة. تعكس هذه العلامات طريقة تعامل Compose مع الدالة أو النوع أثناء إعادة التركيب.

الدوال

يمكن أن تصنّف "الكتابة الذكية" الدوال على أنّها skippable أو restartable. يُرجى العِلم أنّه قد يتم تصنيف الدالة على أنّها إحدى الحالتين التاليتين أو كلتيهما أو لا شيء منهما:

  • قابلة للتخطّي: إذا وضع المترجم علامة على عنصر قابل للإنشاء باعتباره قابلاً للتخطّي، يمكن أن يتخطّاه Compose أثناء إعادة التركيب إذا كانت جميع وسيطاته تساوي قيمها السابقة. (في حال تفعيل وضع التخطّي القوي في Kotlin 2.0.20 أو الإصدارات الأحدث، يمكن تلقائيًا تخطّي جميع العناصر القابلة لإعادة التشغيل.)
  • قابلة لإعادة التشغيل: الدالة القابلة لإعادة التشغيل هي دالة مركّبة تعمل كنطاق يمكن أن تبدأ فيه عملية إعادة التركيب. بعبارة أخرى، يمكن أن تكون الدالة نقطة دخول يمكن أن يبدأ منها Compose في إعادة تنفيذ الرمز البرمجي لإعادة التركيب بعد تغييرات الحالة.

الأنواع

يمكن تصنيف أنواع العلامات المركّبة على أنّها غير قابلة للتغيير أو ثابتة. يكون كل نوع من النوعين التاليين:

  • غير قابل للتغيير: تحدّد Compose نوعًا على أنّه غير قابل للتغيير إذا كانت قيمة خصائصه لا يمكن تغييرها مطلقًا وكانت جميع الطرق شفافة من الناحية المرجعية.
    • يُرجى العلم أنّه تم تصنيف جميع الأنواع الأساسية على أنّها غير قابلة للتغيير. وتشمل هذه المناطق String وInt وFloat.
  • ثابت: يشير إلى نوع يمكن تغيير خصائصه بعد الإنشاء. وعندما تتغيّر هذه الخصائص أثناء وقت التشغيل، يصبح Compose على دراية بهذه التغييرات.

ثبات تصحيح الأخطاء

إذا كان تطبيقك يعيد إنشاء دالة مركّبة لم تتغيّر مَعلماتها، عليك أولاً التحقّق مما إذا كان يتم تخصيص مثيلات جديدة من الأنواع غير الثابتة في كل عملية إعادة التكوين (أو، إذا كان وضع "التخطّي القوي" غير مفعَّل، عليك التحقّق مما إذا كانت الدالة المركّبة تتضمّن مَعلمات ذات خصائص var أو خصائص val من نوع غير ثابت).

للحصول على معلومات تفصيلية حول كيفية تشخيص المشاكل المعقّدة المتعلّقة بالثبات في Compose، راجِع دليل تصحيح أخطاء الثبات.

حلّ المشاكل المتعلّقة بالثبات

للحصول على معلومات حول كيفية تحقيق الثبات في عملية تنفيذ Compose، راجِع دليل حلّ مشاكل الثبات.

ملخّص

بشكل عام، يجب مراعاة النقاط التالية:

  • المَعلمات: يحدّد Compose ثبات كل مَعلمة من مكوّناتك القابلة للإنشاء لتحديد ما إذا كان سيتم مقارنتها باستخدام المساواة البنيوية (.equals()) أو المساواة في المثيل (===) أثناء إعادة التركيب.
  • حلول فورية: إذا لاحظت أنّه لا يتم تخطّي الدالة المركّبة وأنّه يتسبّب في حدوث مشكلة في الأداء، تحقَّق ممّا إذا كان يتم إعادة إنشاء مثيلات جديدة للمعلمات غير الثابتة في كل عملية تمرير أو إذا كان يتم استخدام سمات var بدلاً من State.
  • تقارير المحول البرمجي: يمكنك استخدام تقارير المحول البرمجي لتحديد مستوى الثبات الذي يتم استنتاجه بشأن صفوفك.
  • المجموعات: تعتبر Compose واجهات المجموعات العادية (List وSet وMap) غير ثابتة لأنّ عمليات التنفيذ الأساسية قد تكون قابلة للتغيير. باستخدام وضع التخطّي القوي (مفعَّل تلقائيًا في Kotlin 2.0.20 والإصدارات الأحدث)، تظل المجموعات غير الثابتة تتخطّى إعادة التركيب باستخدام ميزة O(1)المقارنة السريعة بين مثيلين=== عند عدم تغيير مرجع المجموعة. تجنَّب تحويل المجموعات الكبيرة إلى مجموعات Kotlinx غير قابلة للتغيير أو إضافة التعليقات التوضيحية @Immutable أو @Stable إلى الفئات التي تتضمّن مجموعات، إلا إذا كانت هناك حاجة إلى المساواة البنيوية (O(N) .equals())، لأنّ مقارنة كل عنصر في كل عملية إعادة إنشاء يمكن أن تكون أكثر تكلفة من عملية إعادة الإنشاء نفسها.
  • الوحدات الأخرى: تعتبر Compose أنّ الفئات من الوحدات التي لا يتم فيها تشغيل برنامج التحويل البرمجي Compose غير ثابتة (تتم المقارنة باستخدام === في وضع التخطّي الصارم). إذا كان مصدر البيانات يعيد إنشاء نماذج صغيرة ومسطّحة بشكل متكرّر من وحدات غير تابعة لـ Compose بقيم متطابقة، يمكنك إعداد ملف إعدادات الثبات أو استخدام @Stable أو @Immutable حيث تكون .equals() غير مكلفة.

محتوى إضافي للقراءة