پایداری در Compose

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

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

  • پارامترهای پایدار: Compose پارامترهای پایدار را بااستفاده از برابری ساختاری (Object.equals()) مقایسه می‌کند. اگر پارامترهای پایدار یک composable با مقادیر قبلی‌شان برابر باشند، Compose از آن رد می‌شود.
  • پارامترهای ناپایدار: با فعال بودن حالت پرش قوی (پیش‌فرض در Kotlin نسخه ۲.۰.۲۰ و بالاتر)، Compose پارامترهای ناپایدار را بااستفاده از برابری نمونه (===) مقایسه می‌کند و اگر نمونه‌های شیء یکسان ارسال شوند، از composable رد می‌شود. در نسخه‌های قبل‌از Kotlin 2.0.20 (یا وقتی حالت «پرش قوی» غیرفعال است)، Compose همیشه یک composable با پارامترهای ناپایدار را وقتی والد آن بازترکیب می‌شود، بازترکیب می‌کند.

اگر برنامه‌تان به‌طور مکرر نمونه‌های جدیدی از پارامترهای ناپایدار را اختصاص می‌دهد—یا با حاشیه‌نویسی بیش‌ازحد مدل‌ها، بررسی‌های پرهزینه .equals() را بر مجموعه‌های بزرگ تحمیل می‌کند—ممکن است شاهد بازترکیب‌های غیرضروری یا سربار مقایسه باشید.

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

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

تکه‌کدهای زیر اصول کلی پایداری و بازترکیب را نشان می‌دهند.

کلاس Contact یک کلاس داده تغییرناپذیر است. دلیلش این است که همه پارامترهای آن از نوع اولیه هستند که با کلیدواژه val تعریف شده‌اند. وقتی نمونه‌ای از Contact ایجاد می‌کنید، نمی‌توانید مقدار ویژگی‌های شیء را تغییر دهید. اگر برای انجام این کار تلاش کنید، شیء جدیدی ایجاد خواهید کرد.

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

ContactRow composable پارامتری از نوع 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 فقط تغییرات اشیاء State در Compose را ردیابی می‌کند.

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

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

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

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

توابع

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

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

انواع

‫Compose انواع را به‌عنوان تغییرناپذیر یا پایدار علامت‌گذاری می‌کند. هر نوع یکی از این دو است:

  • تغییرناپذیر: Compose نوعی را به‌عنوان تغییرناپذیر علامت‌گذاری می‌کند اگر مقدار ویژگی‌های آن هرگز تغییر نکند و همه روش‌ها ازنظر ارجاعی شفاف باشند.
    • توجه داشته باشید که همه انواع اولیه به‌عنوان تغییرناپذیر علامت‌گذاری شده‌اند. این موارد عبارت‌اند از: String، Int، و Float.
  • Stable: نوعی را نشان می‌دهد که ویژگی‌های آن می‌تواند پس‌از ساخت تغییر کند. اگر و زمانی که این ویژگی‌ها درطول زمان اجرا تغییر کنند، Compose از این تغییرات مطلع می‌شود.

پایداری اشکال‌زدایی

اگر برنامه‌تان درحال بازترکیب یک ترکیب‌پذیر است که پارامترهای آن تغییر نکرده است، ابتدا بررسی کنید که آیا نمونه‌های جدیدی از انواع ناپایدار در هر بازترکیب اختصاص داده می‌شود یا نه (یا اگر حالت «پرش قوی» غیرفعال است، آیا ترکیب‌پذیر پارامترهایی با ویژگی‌های var یا ویژگی‌های val از نوع ناپایدار دارد یا نه).

برای اطلاعات دقیق درباره نحوه تشخیص مشکلات پیچیده مربوط به پایداری در Compose، راهنمای اشکال‌زدایی پایداری را ببینید.

برطرف کردن مشکلات پایداری

برای کسب اطلاعات درباره نحوه ایجاد پایداری در پیاده‌سازی Compose، راهنمای رفع مشکلات پایداری را ببینید.

خلاصه

به‌طورکلی، باید به نکات زیر توجه کنید:

  • پارامترها: Compose پایداری هر پارامتر از composable شما را تعیین می‌کند تا تصمیم بگیرد که آیا آن‌ها را بااستفاده از برابری ساختاری (.equals()) یا برابری نمونه (===) درطول بازترکیب مقایسه کند یا نه.
  • رفع مشکل فوری: اگر متوجه شدید که از composable شما رد نمی‌شود و این امر باعث مشکل عملکرد می‌شود، بررسی کنید که آیا نمونه‌های جدیدی از پارامترهای ناپایدار در هر گذر بازسازی می‌شوند یا اینکه به‌جای State از ویژگی‌های var استفاده می‌شود.
  • گزارش‌های کامپایلر: می‌توانید از گزارش‌های کامپایلر برای تعیین اینکه چه پایداری‌ای درباره کلاس‌های شما استنباط می‌شود استفاده کنید.
  • مجموعه‌ها: Compose میاناهای مجموعه استاندارد (List، Set، و Map) را ناپایدار درنظر می‌گیرد زیرا پیاده‌سازی‌های اساسی آن‌ها ممکن است تغییرپذیر باشد. با حالت رد شدن قوی (به‌طور پیش‌فرض در Kotlin نسخه 2.0.20+ فعال است)، مجموعه‌های ناپایدار همچنان بااستفاده از برابری نمونه (===) وقتی مرجع مجموعه تغییر نمی‌کند، از بازترکیب رد می‌شوند. در جایگزین کردن مجموعه‌های استاندارد با مجموعه‌های تغییرناپذیر Kotlinx یا حاشیه‌نویسی کلاس‌های نگهدارنده مجموعه با @Immutable یا @Stable احتیاط کنید، زیرا مقایسه هر عنصر با .equals() در هر گذر بازترکیب می‌تواند پرهزینه‌تر از خود بازترکیب باشد.
  • سایر پیمانه‌ها: Compose کلاس‌های پیمانه‌هایی را که کامپایلر Compose در آن‌ها اجرا نمی‌شود ناپایدار درنظر می‌گیرد (بااستفاده از === در حالت «پرش قوی» مقایسه می‌شود). اگر منبع داده‌ای به‌طور مکرر مدل‌های کوچک و مسطح را از ماژول‌های غیرCompose با مقادیر یکسان بازنمونه‌سازی می‌کند، می‌توانید یک فایل پیکربندی پایداری را پیکربندی کنید یا از @Stable یا @Immutable استفاده کنید که در آن .equals() ارزان است.

مطالعه بیشتر