پایداری در Compose

‫Compose انواع را پایدار یا ناپایدار درنظر می‌گیرد. نوعی پایدار است اگر تغییرناپذیر باشد، یا اگر Compose بتواند تشخیص دهد مقدار آن بین ترکیب‌های مجدد تغییر کرده است یا نه. اگر Compose نتواند تشخیص دهد که مقدار نوعی بین بازآفرینی‌ها تغییر کرده است یا نه، آن نوع ناپایدار است.

‫Compose از پایداری پارامترهای تابع ترکیب‌پذیر برای تعیین نحوه مقایسه ورودی‌ها و تصمیم‌گیری درباره اینکه آیا می‌تواند تابع ترکیب‌پذیر را درطول ترکیب مجدد رد کند (با فعال بودن حالت رد کردن قوی به‌طور پیش‌فرض از Kotlin 2.0.20):

  • پارامترهای پایدار: Compose پارامترهای پایدار را بااستفاده از برابری ساختاری (Object.equals()) مقایسه می‌کند. اگر پارامترهای پایدار یک عنصر ترکیبی با مقادیر قبلی آن برابر باشد، Compose از آن صرف‌نظر می‌کند.
  • پارامترهای ناپایدار: با فعال بودن حالت رد کردن قوی (پیش‌فرض در Kotlin نسخه ۲.۰.۲۰ و بالاتر)، Compose پارامترهای ناپایدار را بااستفاده از برابری نمونه (===) مقایسه می‌کند و اگر نمونه‌های شیء یکسانی ارسال شود، تابع ترکیبی را رد می‌کند. در نسخه‌های قبل‌از Kotlin 2.0.20 (یا زمانی که حالت «پرش قوی» غیرفعال باشد)، وقتی عنصر قابل ترکیب والد بازترکیب می‌شود، 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. بنابراین، «نوشتن» از ContactDetails رد می‌شود و آن را بازنویسی نمی‌کند.
  5. ازطرف دیگر، آرگومان‌های ToggleButton تغییر کرده است و ‫Compose آن عنصر را دوباره می‌سازد.

اشیاء تغییرپذیر

اگرچه مثال قبلی از یک شیء تغییرناپذیر استفاده می‌کند، اما می‌توان یک شیء تغییرپذیر ایجاد کرد. تکه‌کد زیر را درنظر بگیرید:

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

ازآنجایی‌که هر پارامتر Contact اکنون var است، کلاس دیگر تغییرناپذیر نیست. اگر ویژگی‌های آن تغییر کند، Compose متوجه نخواهد شد. دلیل این امر این است که ‫Compose فقط تغییرات در اشیاء حالت Compose را پیگیری می‌کند.

‫Compose چنین کلاسی را ناپایدار درنظر می‌گیرد. با حالت رد کردن قوی (Kotlin 2.0.20+)،‏ Compose بااستفاده از برابری نمونه (===) Contact را مقایسه می‌کند: تغییر contact.name در محل باعث راه‌اندازی بازآفرینی نمی‌شود، درحالی‌که انتقال نمونه Contact جدیداً تخصیص‌یافته با مقادیر یکسان همچنان باعث می‌شود ContactDetails مجبور به بازآفرینی شود (و بدون «حالت رد کردن قوی»، ContactDetails هر بار که selected تغییر می‌کند بازآفرینی می‌شود).

پیاده‌سازی در Compose

درنظر گرفتن اینکه Compose دقیقاً چگونه تعیین می‌کند که کدام توابع را درطول ترکیب مجدد رد کند می‌تواند مفید باشد، اگرچه حیاتی نیست.

وقتی کامپایلر Compose روی کد شما اجرا می‌شود، هر تابع و نوع را با یکی از چند برچسب علامت‌گذاری می‌کند. این برچسب‌ها نشان می‌دهند که Compose چگونه تابع یا نوع را درطول ترکیب مجدد مدیریت می‌کند.

توابع

«ترکیب» می‌تواند توابع را به‌عنوان skippable یا restartable علامت‌گذاری کند. توجه داشته باشید که ممکن است تابعی را به‌عنوان یکی از این موارد، هر دو مورد، یا هیچ‌کدام علامت‌گذاری کند:

  • قابل رد کردن: اگر گردآورنده یک عنصر ترکیبی را به‌عنوان قابل رد کردن علامت‌گذاری کند، Compose می‌تواند آن را درطول ترکیب مجدد رد کند اگر همه آرگومان‌های آن با مقادیر قبلی‌شان برابر باشند. (با حالت رد کردن قوی در Kotlin 2.0.20 و نسخه‌های بالاتر، همه ترکیب‌های قابل‌راه‌اندازی مجدد به‌طور خودکار قابل رد کردن هستند.)
  • بازراه‌اندازی‌شدنی: عنصر ترکیبی بازراه‌اندازی‌شدنی به‌عنوان «محدوده» عمل می‌کند که ترکیب مجدد می‌تواند از آنجا شروع شود. به‌عبارت دیگر، این تابع می‌تواند نقطه ورودی برای جایی باشد که Compose می‌تواند پس‌از تغییرات حالت، اجرای مجدد کد را برای ترکیب مجدد شروع کند.

انواع

‫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() ارزان است استفاده کنید.

مطالعه بیشتر