طراحی سایت و برنامه نویسی

آموزش طراحی سایت و برنامه نویسی

طراحی سایت و برنامه نویسی

آموزش طراحی سایت و برنامه نویسی

راهنمای جامع تایپ اسکریپت (Typescript) — از صفر تا صد

t یک ابرمجموعه نوع‌بندی شده از جاوا اسکریپت و هدف آن تسهیل توسعه اپلیکیشن‌های بزرگ جاوا اسکریپت است. تایپ اسکریپت مفاهیم رایجی مانند کلاس‌ها، ژنریک‌ها، اینترفیس‌ها و انواع استاتیک را به جاوا اسکریپت می‌افزاید و به توسعه‌دهندگان امکان می‌دهد که از ابزارهایی همچون بررسی استاتیک و «بازسازی» (Refactoring) کد استفاده کنند.

Typescript چه اهمیتی دارد؟

در واقع سؤال فوق را می‌توان این گونه نیز بیان کرد که چرا باید از تایپ اسکریپت استفاده کرد؟ در ادامه برخی از دلایلی که توسعه‌دهندگان جاوا اسکریپت باید یادگیری Typescript را در نظر داشته باشند توضیح می‌دهیم.

نوع‌بندی استاتیک: جاوا اسکریپت یک زبان برنامه‌نویسی با نوع‌بندی دینامیک است یعنی تا زمانی که وهله‌ای از یک متغیر را در زمان اجرا ایجاد نکنید در مورد نوع متغیر اطلاعی ندارد و همین مسئله موجب بروز مشکلات و خطاهایی در پروژه می‌شود. Typescript پشتیبانی از نوع‌بندی استاتیک را به جاوا اسکریپت اضافه کرده است که موجب می‌شود از بروز باگ‌هایی که ناشی از تصور نادرست در مورد نوع متغیر هستند جلوگیری شود. ما همچنان در صورتی که از نوع متغیر استفاده کنیم، کنترل کاملی روی میزان صراحت نوع متغیر در کد خود داریم.

پشتیبانی بهتر از IDE: یکی از بزرگ‌ترین مزیت‌های Typescript نسبت به جاوا اسکریپت، پشتیبانی عالی از IDE است که شامل Intellisense، اطلاعات آنی از کامپایلر Typescript، دیباگ کردن و مواردی از این دست می‌شود. همچنین برخی اکستنشن‌های عالی برای ارتقای بیشتر تجربه توسعه Typescript وجود دارند.

دسترسی به قابلیت‌های جدید ECMAScript: تایپ‌اسکریپت امکان دسترسی به جدیدترین قابلیت‌های ECMAScript را می‌دهد و آن‌ها را به اهداف ECMAScript مورد نظر شما تبدیل می‌کند. این بدان معنی است که می‌توانید اپلیکیشن‌ها را با استفاده از جدیدترین ابزارها بدون نیاز به نگرانی در مورد پشتیبانی از مرورگر توسعه دهید.

چه هنگام باید از Typescript استفاده کنیم؟

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

زمانی که کدبیس بزرگی وجود دارد: تایپ‌اسکریپت یک ارتقای مهم برای کدبیس های بزرگ محسوب می‌شود، چون کمک می‌کند از بروز بسیاری از خطاهای رایج پیشگیری کنیم. این واقعیت به طور خاص در مواردی که توسعه‌دهندگان زیادی روی پروژه منفرد کار کنند صدق می‌کند.

زمانی که شما و تیم‌تان از قبل با زبان‌های دارای نوع‌بندی استاتیک آشنا باشید: موقعیت بدیهی دیگری که استفاده از تایپ‌اسکریپت در آن توصیه می‌شود هنگامی است که شما و تیمتان با زبان‌های دارای نوع‌بندی استاتیک مانند جاوا و C# آشنا باشید و نخواهید از روش نگارش کدهای جاوا اسکریپت استفاده کنید.

راه‌اندازی Typescript

برای راه‌اندازی تایپ‌اسکریپت، کافی است آن را با ابزار مدیریت بسته npm نصب کرده و یک فایل تایپ‌اسکریپت جدید بسازیم:

npm install -g typescript

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

نوع‌ها

اینک نوبت آن رسیده است که ببینیم تایپ‌اسکریپت چه نوع‌هایی را در اختیار ما قرار می‌دهد.

نوع عددی

همه اعداد در تایپ‌اسکریپت مقادیر اعشاری هستند. همه این مقادیر نوع عددی می‌گیرند و این وضعیت شامل مقادیر باینری و هگزادسیمال نیز می‌شود.

نوع رشته

تایپ‌اسکریپت مانند زبان‌های دیگر از نوع داده String برای ذخیره داده‌های متنی استفاده می‌کند.

شما در تایپ‌اسکریپت می‌توانید با قرار دادن رشته درون بک‌تیک () از رشته‌های چندخطی و همچنین عبارت‌های جاسازی‌شده (embed) استفاده کنید.

نوع بولی

تایپ‌اسکریپت از اغلب انواع داده ابتدایی پشتیانی می‌کند که شامل مقادیر بولی نیز می‌شوند. این مقادیر تنها می‌توانند شامل دو مقدار درست و نادرست باشند.

انتساب نوع

اینک که با انواع داده ابتدایی آشنا شدیم، می‌توانیم به بررسی روش انتساب نوع‌ها در تایپ‌اسکریپت بپردازیم. بدین منظور اساساً کافی است که نوع متغیر را پس از نام آن و یک کاراکتر دونقطه (:) بنویسید.

نوع منفرد

در ادامه مثالی را مشاهده می‌کنید که نوع داده رشته‌ای را به یک متغیر انتساب می‌دهد:

این قالب در مورد همه انواع متغیر صدق می‌کند.

انواع چندگانه

با استفاده از عملگر (|) امکان انتساب چند نوع داده مختلف به متغیرها نیز وجود دارد:

در این روش دو نوع را با استفاده از عملگر | به متغیر خود انتساب می‌دهیم. بدین ترتیب می‌توانیم در آن رشته و همچنین عدد ذخیره کنیم.

بررسی نوع‌ها

اکنون نوبت آن رسیده است که بررسی کنیم آیا متغیرهای ما نوع صحیحی دارند یا نه. ما چندین گزینه به این منظور داریم، اما تنها دو مورد از انواع پراستفاده یعنی Typeof و Instanceof را بررسی می‌کنیم.

Typeof

دستور Typeof تنها با انواع داده ابتدایی آشنایی دارد. این بدان معنی است که این دستور تنها می‌تواند متغیری را از یکی از انواع داده که در بخش قبل اشاره کردیم بررسی کند:

در این مثال یک متغیر رشته‌ای ایجاد می‌کنیم و از دستور typeof استفاده می‌کنیم تا بررسی کنیم که آیا str از نوع عددی است یا نه (که همواره نادرست است). سپس عدد بودن یا نبودن آن را نمایش می‌دهیم.

Instanceof

عملگر Instanceof تقریباً همانند Typeof است و تنها تفاوت این است که می‌تواند انواع سفارشی را که از قبل از سوی جاوا اسکریپت تعریف نشده‌اند را نیز بررسی کند.

در کد فوق یک نوع سفارشی داریم که در ادامه این مقاله آن را بیشتر بررسی خواهیم کرد. فعلاً یک وهله از آن می‌سازیم و سپس بررسی می‌کنیم که آیا واقعاً متغیری از نوع Human است یا نه و نتیجه را در کنسول نمایش می‌دهیم.

تأکید بر نوع

برخی اوقات لازم می‌شود که متغیرهای خود را به نوع‌های خاصی تبدیل کنیم. این موارد غالباً زمانی رخ می‌دهند که یک نوع عمومی مانند any انتساب داده باشیم و بخواهیم تابع‌هایی از نوع خاص استفاده کنیم. چندین گزینه برای حل این مشکل وجود دارد، اما در ادامه تنها دو مورد را بررسی می‌کنیم.

کلیدواژه as

با استفاده از کلیدواژه as پس از نام متغیر و سپس اشاره به نوع داده جدید می‌توان به سادگی، نوع یک متغیر را تغییر داد.

عملگر <>

همچنین می‌توانیم از عملگر <> استفاده کنیم که دقیقاً همان تأثیر کلیدواژه as را با ساختاری متفاوت به دست می‌دهد.

این قطعه کد دقیقاً همان کارکرد قطعه کد قبلی را دارد. تنها ساختار آن متفاوت است.

آرایه‌ها

آرایه‌ها در تایپ‌اسکریپت مجموعه‌ای از اشیای یکسان هستند و می‌توانند به دو روش متفاوت ایجاد شوند.

ایجاد آرایه‌ها

روش‌های مختلف ایجاد آرایه را در ادامه مشاهده می‌کنید.

با استفاده از []

می‌توان یک آرایه را با نوشتن نوع و سپس [] برای نمایش این که یک آرایه است ایجاد کرد.

در این مثال، یک آرایه رشته‌ای می‌سازیم که سه مقدار رشته‌ای متفاوت را در خود نگهداری می‌کند.

با استفاده از نوع آرایه ژنریک

امکان تعریف یک آرایه با استفاده از نوع ژنریک به صورت نوشتن Array<Type> نیز وجود دارد:

در این روش یک آرایه عددی می‌سازیم که 5 مقدار عددی متفاوت را نگهداری می‌کند.

آرایه‌های چندگانه

به علاوه می‌توانیم انواع چندگانه‌ای را با استفاده از یک عملگر | به یک آرایه انتساب دهیم.

در این مثال، یک آرایه ایجاد کرده‌ایم که می‌تواند مقادیر رشته‌ای و عددی را نگه‌داری کنید.

آرایه‌های چندبعدی

تایپ‌اسکریپت امکان تعریف آرایه چندبعدی را نیز می‌دهد و معنی آن این است که می‌توانیم یک آرایه را درون آرایه دیگر ذخیره کنیم. بدین ترتیب می‌توانیم آرایه چندبعدی را با استفاده از عملگرهای چندگانه [] پس از همدیگر تعریف کنیم.

در مثال فوق یک آرایه تعریف کرده‌ایم که آرایه عددی دیگری را نگهداری می‌کند.

چندتایی

چندتایی‌ها اساساً شبیه به آرایه هستند و تنها یک تفاوت کلیدی با آن دارند. تفاوت این است که می‌توان نوع داده‌ای که در هر موقعیت ذخیره خواهد شد را تعیین کرد و این بدان معنی است که با شمارش انواع مختلف درون براکت‌های مربعی، می‌توانیم انواعی برای اندیس‌ها تعیین کنیم.

در مثال فوق یک چندتایی ساده ساخته‌ایم که در اندیس 0 عدد و در اندیس 1 رشته دارد. این بدان معنی است که اگر تلاش کنیم نوع داده متفاوتی در این اندیس قرار دهیم با خطایی مواجه خواهیم شد.

در ادامه مثالی از یک چندتایی نامعتبر را می‌بینید:

Enum-ها

Enum-ها در تایپ‌اسکریپت شبیه به دیگر زبان‌های برنامه‌نویسی شیءگرا هستند و امکان تعریف کردن مجموعه‌ای از ثابت‌های دارای نام را به ما می‌دهند. تایپ‌اسکریپت هم Enum-های مبتنی بر عدد و هم مبتنی بر رشته دارد. Enum-ها در تایپ‌اسکریپت با استفاده از کلیدواژه enum تعریف می‌شوند.

عددی

ابتدا نگاهی به enum-های عددی خواهیم داشت که در آن مقدار یک کلید با یک اندیس تطبیق می‌یابد.

در کد فوق اقدام به تعریف یک enum عددی می‌کنیم که Playing با 0 مقداردهی می‌شود، مقدار Paused برابر با 1 و همین طور تا آخر است.

همچنین می‌توانیم مقداردهی را خالی بگذاریم تا تایپ‌اسکریپت به صورت خودکار شروع به اندیس‌گذاری از صفر بکند.

رشته

تعریف کردن یک enum رشته‌ای در تایپ‌اسکریپت کار آسانی است و کافی است مقادیر را با رشته‌ها مقداردهی کنیم.

در کد فوق یک enum رشته‌ای را با مقداردهی States به وسیله رشته‌ها تعریف کرده‌ایم.

شییءها

منظور از شیء در جاوا اسکریپت وهله‌ای است که شامل مجموعه‌ای از جفت‌های کلید-مقدار است. این مقادیر می‌توانند متغیر، آرایه یا حتی تابع باشند. همچنین آن را می‌توان به صورت نوع داده‌ای تصور کرد که انواع غیر مقدماتی را نمایش می‌دهد.

با استفاده از آکولاد می‌توان شیء ایجاد کرد:

در کد فوق یک شیء human ساخته‌ایم که سه جفت مختلف کلید-مقدار دارد. همچنین می‌توانیم تابع‌هایی را به شیء خود اضافه کنیم:

انواع سفارشی

تایپ‌اسکریپت امکان تعریف انواع سفارشی را نیز فراهم ساخته است که «اسم مستعار» (alias) نام دارد و می‌توان در ادامه به سهولت از آن‌ها استفاده مجدد کرد. برای ایجاد نوع سفارشی کافی است از کلیدواژه type استفاده کرده و نوع مورد نظر خود را تعریف کنیم.

در این مثال، به تعریف یک نوع سفارشی با نام Human و مشخصات مورد نظر می‌پردازیم. اینک نگاهی به روشی ایجاد یک شیء با این نوع خواهیم داشت:

در کد فوق یک وهله از نوع سفارشی ایجاد کرده و مشخصه‌های مورد نیاز را تعیین می‌کنیم.

پارامترهای تابع و نوع‌های بازگشتی

تایپ‌اسکریپت به ما امکان می‌دهد که انواعی برای پارامترهای تابع خود تعیین کرده و نوع بازگشتی را تعیین کنیم. اینک نگاهی به ساختار مورد نیاز برای تعریف کردن یک تابع با استفاده از تایپ‌اسکریپت خواهیم داشت.

در کد فوق دو تابع نمونه داریم که هر دو پارامترهایی با انواع تعریف شده دارند. همچنین می‌بینیم که نوع بازگشتی پس از بستن پرانتزها تعریف شده است.

اکنون می‌توانیم تابع خود را مانند یک تابع معمولی جاوا اسکریپت فراخوانی کنیم، اما کامپایلر بررسی خواهد کرد که آیا تابع با پارامترهای صحیحی ارائه شده است یا نه.

مشخصه‌های اختیاری

تایپ‌اسکریپت امکان تعریف کردن مشخصه‌های اختیاری را نیز برای تابع می‌دهد. این کار با استفاده از عملگر ? صورت می‌گیرد نمونه‌ای از آن به صورت زیر است:

در مثال فوق lastName یک پارامتر اختیاری است و معنی آن این است که در صورتی که در زمان فراخوانی تابع به آن اشاره نکنیم، کامپایلر خطایی صادر نخواهد کرد.

این بدان معنی است که هر دو این حالت‌ها صحیح تلقی می‌شوند.

مقادیر پیش‌فرض

متد دومی که می‌توانیم برای ایجاد یک مشخصه اختیاری استفاده کنیم از طریق انتساب آن به یک مقدار پیش‌فرض است. این کار از طریق انتساب دادن مقدار مستقیماً به عنوان یک تابع میسر خواهد بود.

در این مثال، ما یک مقدار پیش‌فرض را به lastName انتساب داده‌ایم. معنی آن این است که نمی‌خواهیم آن را در هر بار فراخوانی تابع ارائه کنیم.

اینترفیس‌ها

اینترفیس‌ها در تایپ‌اسکریپت برای تعریف قراردادهایی با خود کد و همچنین کد خارج از پروژه استفاده می‌شوند. اینترفیس‌ها تنها شامل اعلان‌هایی از متدها و مشخصه‌ها هستند، اما آن‌ها را پیاده‌سازی نمی‌کنند. پیاده‌سازی متدها و مشخصه‌ها در مسئولیت کلاسی است که اینترفیس را پیاده‌سازی می‌کند.

در ادامه نگاهی به مثالی از ساخت این گزاره‌ها خواهیم داشت تا مسائل کمی روشن‌تر شوند:

در کد فوق یک اینترفیس با یک مشخصه ایجاد می‌شود که باید در زمان پیاده‌سازی اینترفیس، پیاده‌سازی شود. به همین دلیل است که متغیر person دوم یک خطا ایجاد می‌کند.

مشخصه‌های اختیاری

در تایپ‌اسکریپت همه مشخصه‌های یک اینترفیس الزامی نیستند. مشخصه‌ها می‌توانند با استفاده از عملگر ? پس از نام مشخصه به صورت اختیاری تعیین شوند.

در ادامه یک اینترفیس با یک مشخصه نرمال و یک مشخصه اختیاری ایجاد می‌کنیم که با استفاده از عملگر ? تعریف می‌شود. به همین دلیل است که هر دو مقداردهنده‌های person معتبر هستند.

مشخصه‌های فقط-خواندنی

برخی مشخصه‌های اینترفیس باید تنها هنگامی اصلاح شوند که شیء در وهله نخست ایجاد می‌شود. ما می‌توانیم این کارکرد را از طریق قرار دادن کلیدواژه readonly قبل از نام مشخصه تعیین کنیم.

در این مثال، مشخصه id فقط-خواندنی است و نمی‌توان آن را پس از ایجاد شیء تغییر داد.

Barrels

Barrels امکان جمع‌بندی چند ماژول اکسپورت در یک ماژول منفرد ساده‌تر را فراهم می‌سازند. به این منظور کافی است یک فایل جدید ایجاد کنیم که ماژول‌های چندگانه اکسپورت پروژه در آن قرار خواهند گرفت.

پس از انجام این کار می‌توانیم همه این ماژول‌ها را با استفاده از یک گزاره منفرد import، در پروژه ایمپورت کنیم.

ژنریک‌ها

ژنریک‌ها امکان ایجاد کامپوننت‌هایی را می‌دهند که با طیف گسترده‌ای از نوع‌ها منطبق هستند و اختصاص به یک نوع ندارند. بدین ترتیب کامپوننت ما open می‌شود و قابلیت استفاده مجدد پیدا می‌کند. اینک ممکن است کنجکاو شوید که چرا مستقیماً از یک نوع استفاده نمی‌کنیم که چند نوع منفرد برای کامپوننت ما ارائه کند. در ادامه به بررسی یک مثال می‌پردازیم تا این موقعیت را بهتر درک کنیم. ما می‌خواهیم یک تابع ساختگی ساده داشته باشیم که پارامتری که به آن تجزیه می‌شود را بازگشت دهد.

با این که any یک ژنریک است و هر نوعی که برای آرگومان ارسال شود قبول می‌کند، اما یک تفاوت بزرگ وجود دارد. در این روش ما اطلاعات نوع داده ارسالی و نوع بازگشتی تابع را از دست می‌دهیم. بنابراین در ادامه به بررسی روش پذیرش همه انواع و در عین حال حفظ اطلاعات در مورد نوع بازگشتی می‌پردازیم.

در کد فوق از یک پارامتر ژنریک T استفاده کرده‌ایم و از این رو می‌توانیم نوع متغیر را دریافت کرده و آن را در ادامه استفاده کنیم. همچنین می‌توانیم از آن به عنوان پارامتر بازگشتی خود استفاده کنیم که به ما امکان می‌دهد نوع متناظر را در زمان بازبینی کد ببینیم.

مادیفایرهای دسترسی

مادیفایرهای دسترسی به کنترل دسترس‌پذیری اعضای کلاس می‌پردازند. تایپ‌اسکریپت از سه نوع مادیفایر دسترسی به صورت  public ،private و protected پشتیبانی می‌کند.

Public: اعضای عمومی یا Public بدون هیچ محدودیتی در همه جا در دسترس هستند. این مادیفایر یک مادیفایر استاندارد محسوب می‌شود، یعنی لازم نیست در زمان تعریف متغیرهای عمومی از کلیدواژه Public استفاده کنید.

Private: اعضای خصوصی تنها از درون کلاسی که در آن تعریف شده‌اند قابل دسترسی هستند.

Protected: اعضای حفاظت شده می‌توانند تنها از درون کلاسی که در آن تعریف شده‌اند و همچنین کلاس‌های فرعی و فرزند آن مورد دسترسی قرار گیرند.

TSLINT

TSLINT یک linter استاندارد برای تایپ‌اسکریپت است و به نوشتن کدهای تمیز، قابل نگهداری و خوانا کمک می‌کند. آن را می‌توانیم با قواعد lint، پیکربندی و قالب‌بندی‌های خاص که خودمان می‌نویسیم به صورت سفارشی درآوریم.

نصب TSLINT

ابتدا باید تایپ‌اسکریپت و tslint را نصب کنیم و این کار به صورت محلی یا سراسری ممکن است:

npm install tslint typescript --save-dev

npm install tslint typescript –g

پس از آن می‌توانیم از TSLINT CLI برای مقداردهی اولیه TSLINT در پروژه خود استفاده کنیم:

tslint –init

اینک که فایل tslint.json خود را ساختیم، می‌توانیم شروع به پیکربندی قواعد خاص خود بکنیم.

پیکربندی TSLINT

TSLINT امکان پیکربندی قواعد خاص و سفارشی روش نمایش کد را به ما می‌دهد. به صورت پیش‌فرض فایل tslint.json مانند زیر است و تنها قواعد پیش‌فرض در آن قرار دارند:

ما می‌توانیم قواعد خود را با قرار دادن در شیء rules تعریف کنیم:

برای کسب اطلاعات بیشتر در مورد قواعد موجود می‌توانید نگاهی به مستندات رسمی (+) آن بیندازید.

سخن پایانی

بدین ترتیب به پایان این مقاله می‌رسیم. امیدواریم مطالعه این راهنما به شما کمک کرده باشد که با مبانی تایپ‌اسکریپت آشنا شوید و روش استفاده از آن در پروژه‌ها را بدانید.

منبع: فرادرس

نشت حافظه در اپلیکیشن‌ های اندروید — از صفر تا صد

 برنامه نویسی  55 بازدید

ساخت یک اپلیکیشن اندروید کار آسانی است، اما ایجاد یک اپلیکیشن اندروید با کیفیت کاملاً بالا که از نظر حافظه کارآمد باشد چنین نیست. برخی توسعه‌دهندگان علاقه زیادی به تمرکز و یافتن قابلیت‌ها، کارکردها و اجزای UI جدید در اپلیکیشن‌های خود دارند. برخی برنامه‌نویسان تمایل دارند روی چیزی کار کنند که تأثیر دیداری بیشتری داشته باشد تا این که زمان خود را صرف چیزهایی بکنند که اغلب افراد در نگاه نخست ممکن است حتی متوجه آن نشوند. اما این رویه خوبی نیست، شما باید این عادت اولویت‌بندی پایین‌تر موارد مرتبط با بهینه‌سازی اپلیکیشن را ترک کنید. یکی از مهم‌ترین گام‌های بهینه‌سازی اپلیکیشن نیز اصلاح موارد نشت حافظه است.

این وضعیت به صورت طبیعی منجر به تشکیل یک بدهی فنی می‌شود که در بلندمدت، شروع به تأثیرگذاری روی کیفیت اپلیکیشن‌ها می‌کند. شما باید خود را عادت بدهید که به تدریج ذهنیت فوق را که اشاره کردیم کنار بگذارید و بیشتر روی عملکرد اپلیکیشن‌های خود متمرکز شوید.

مفهوم نشت حافظه برای بسیاری از توسعه‌دهندگان آزاردهنده است، چون آن را دشوار، زمان‌بر، خسته‌کننده و غیرضروری می‌پندارند، اما خوشبختانه هیچ کدام از این موارد صحت ندارند. زمانی که شروع به بررسی آن بکنید، متوجه می‌شوید که موضوع کاملاً زیبا و جذابی است.

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

Garbage Collector دوست شما است؛ اما نه همیشه

جاوا زبان پایداری است. در اندروید کدی به زبان‌های C یا ++C نوشته نمی‌شود (البته به جز برخی موارد نادر). بدین ترتیب مجبور نیستیم که کل فرایند تخصیص حافظه و آزادی سازی آن را شخصاً مدیریت کنیم. خوشبختانه جاوا به خوبی از عهده انجام این امور برمی‌آید.

پس اینک نخستین سؤالی که به ذهن می‌آید این است که آیا جاوا یک سیستم مدیریت حافظه اختصاصی درونی دارد که می‌تواند به صورت خودکار حافظه را در صورت استفاده نشدن پاک کند؟ در این صورت چرا ما به عنوان توسعه‌دهنده باید در مورد این موضوع دغدغه داشته باشیم؟ آیا Garbage Collector مستعد خطا است؟

پاسخ سؤال فوق منفی است. Garbage Collector دقیقاً همان طور که طراحی شده است کار می‌کند، اما این اشتباه‌های برنامه‌نویسی خود ما است که برخی اوقات Garbage Collector را از گردآوری بخش‌های استفاده نشده حافظه بازمی‌دارد.

بنابراین باید گفت که اساساً این خطای ما است که منجر به بروز مشکل در حافظه می‌شود. Garbage Collector یکی از برترین دستاوردهای جاوا محسوب می‌شود و از این رو شایسته احترام است.

توضیحی در خصوص Garbage Collector

پیش از آن که در خصوص موضوع مقاله بیشتر توضیح بدهیم، باید اندکی در مورد طرز کار عملی Garbage Collector بدانیم. تئوری کارکرد Garbage Collector کاملاً ساده است، اما آن چه در پشت صحنه اتفاق می‌افتد در پاره‌ای موارد کاملاً پیچیده است. در هر حال جای نگرانی نیست، چون ما به طور عمده روی بخش‌های ساده آن تمرکز خواهیم داشت.

نشت حافظه

هر اپلیکیشن اندروید (یا جاوا) باید یک نقطه آغازین داشته باشد که اشیا از آنجا شروع به وهله‌سازی می‌کنند و متدها فراخوانی می‌شوند. بنابراین می‌توانیم این نقطه آغازین را به عنوان ریشه یا root درخت حافظه تصور کنیم. برخی اشیا مستقیماً یک ارجاع به root دارند و برخی دیگر از آن‌ها وهله‌سازی می‌شوند و ارجاع خود را به این اشیا نگه می‌دارند و همین طور ادامه می‌یابد.

از این رو یک زنجیره از ارجاعات شکل می‌گیرد که درخت حافظه را تشکیل می‌دهند. بنابراین Garbage Collector از ریشه‌های GC کار خود را آغاز می‌کند و اشیا را مستقیماً پیمایش می‌کند یا به صورت غیرمستقیم تا root می‌پیماید. در انتهای این فرایند، برخی اشیا وجود خواهند داشت که هرگز از سوی GC ملاقات نشده‌اند.

این موارد همان Garbage (یا اشیای مرده) هستند و همین‌ها هستند که باید از سوی Garbage Collector دوست‌داشتنی ما گردآوری شوند. تا به اینجا داستان شبیه به یک افسانه ساده کودکان بوده است، اما در ادامه کمی عمیق‌تر می‌شویم تا با جذابیت واقعی کارکرد Garbage Collector آشنا شویم.

نشت حافظه چیست؟

تا به اینجا ایده خلاصه‌ای از ماهیت Garbage Collector و طرز کار عملی مدیریت حافظه در اپلیکیشن‌های اندرویدی به دست آورده‌ایم. اکنون نوبت آن رسیده است که روی موضوع نشت حافظه با جزییات بیشتری تمرکز کنیم.

به بیان ساده نشت حافظه زمانی رخ می‌دهد که یک شیء را مدت‌ها پس از آن که هدف خود را برآورده ساخت نگهداری کنیم. مفهوم واقعی موضوع به همین سادگی است. هر شیء طول عمر خاص خود را دارد که در زمان پایان یافتن، باید از آن خداحافظی کرد و در نتیجه حافظه را ترک می‌کند. اما اگر برخی شیء (-های) دیگر به صورت مستقیم یا غیرمستقیم این شیء را نگه دارند، در این صورت Garbage Collector نمی‌تواند آن را گردآوری کند. این وضعیت همان نشت حافظه است.

اما خبر خوب این است که لازم نیست در مورد وقوع رخداد منفرد نشت حافظه در اپلیکیشن نگران باشید، چون همه نشت‌های حافظه باعث از کار افتادن برنامه شما نمی‌شوند.

برخی نشت‌ها هستند که کاملاً کوچک محسوب می‌شوند (نشت چند کیلوبایت حافظه)، برخی از نشت‌ها نیز در خود فریمورک اندروید رخ می‌دهند که شما نمی‌توانید و نباید آن‌ها را رفع کنید. این موارد عموماً تأثیر اندکی روی عملکرد اپلیکیشن شما دارند و می‌توان آن‌ها را به صورت امنی نادیده گرفت.

اما موارد دیگری هستند که می‌توانند اپلیکیشن را از کار بیندازند و آن را به مقدار زیادی کُند سازند به ترتیبی که به زانو درآید. این نشت‌ها آن‌هایی هستند که باید مراقبشان باشیم.

چرا باید نشت‌های حافظه را رفع کرد؟

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

نشت حافظه

زمانی که کاربر به استفاده از اپلیکیشن شما ادامه می‌دهد، حافظه heap نیز شروع به افزایش می‌کند و اگر نشت حافظه در اپلیکیشن رخ بدهد، در این صورت حافظه استفاده نشده در heap نمی‌تواند از سوی GC رهاسازی شود. بنابراین حافظه heap در اپلیکیشن به صورت مداوم افزایش می‌یابد تا این که به نقطه مرگ می‌رسد و حافظه دیگری را نمی‌توان به اپلیکیشن تخصیص داد. در این مرحله خطای OutOfMemoryError اعلام شده و در نهایت اپلیکیشن از کار می‌افتد.

همچنین باید به خاطر داشته باشید که گردآوری garbage یک فرایند سنگین است و از این رو هر چه garbage collector کمتر اجرا شود، برای اپلیکیشن بهتر است.

زمانی که اپلیکیشن مورد استفاده قرار می‌گیرد و حافظه heap شروع به افزایش می‌کند، یک GC کوتاه اجرا می‌شود و شروع به پاکسازی بی‌درنگ شیءهای مرده می‌کند. در این مرحله این GC-ها به صورت همزمان (روی نخ مجزا) اجرا می‌شوند و اپلیکیشن شما را کُند نمی‌سازند و در کل یک مکث 2 تا 5 میلی‌ثانیه‌ای خواهد داشت.

اما اگر اپلیکیشن با نشت‌های حافظه جدی مواجه باشد که در پشت صحنه پنهان شده باشند، در این صورت GC-های کوتاه قادر به آزادسازی حافظه نخواهند بود و هیپ شروع به افزایش می‌کند، بدین ترتیب نیاز به یک GC بزرگ‌تر وجود خواهد داشت که عموماً موجب یک مکث از نوع «توقف کامل» (stop-the-world) در نخ اصلی اپلیکیشن می‌شوند. این مکث حدود 50 تا 100 میلی‌ثانیه زمان طول می‌کشد و بدین ترتیب باعث می‌شود که اپلیکیشن دچار وقفه جدی شود و برای مدت زمانی تقریباً غیر قابل استفاده شود.

بدین ترتیب اکنون تأثیر این نشت‌های حافظه را که بر روی اپلیکیشن واقع می‌شوند و همچنین علت این که چرا باید به سرعت آن‌ها را رفع کنیم را می‌دانیم. بدین ترتیب کاربران بهترین تجربه کاربری را که شایسته‌اش هستند به دست می‌آورند.

چگونه نشت حافظه را تشخیص دهیم؟

تا به اینجا احتمالاً به طور کامل قانع شده‌اید که باید این نشت‌های حافظه را که به صورت پنهان درون اپلیکیشن شما وجود دارند رفع کنید. اما سؤال این است که چگونه می‌توان آن‌ها را تشخیص داد؟

خبر خوب این است که اندروید استودیو ابزار بسیار مفید و قدرتمندی به این منظور دارد که Monitors نام دارد. در واقع مانیتورهای منفردی وجود دارند که نه تنها برای نظارت بر مصرف حافظه بلکه برای نظارت بر مصرف CPU و GPU نیز استفاده می‌شوند.

نشت حافظه

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

Allocation Tracker در مواردی که بتوانید از آن برای بررسی درصد حافظه تخصیص‌یافته به انواع شیءها در اپلیکیشن استفاده کنید بسیار کارآمد خواهد بود. بدین ترتیب می‌توان ایده روشنی از این که کدام اشیا بخش زیادی از حافظه را اشغال کرده‌اند و باید به طور خاص مورد رسیدگی قرار گیرند، به دست آورد.

اما این کار به تنهایی کافی نیست، چون اکنون باید از گزینه Dump Java Heap برای ایجاد یک heap dump استفاده کنید که در عمل نماینده یک اسنپ‌شات از حافظه در نقطه خاصی از زمان است. چنان که می‌بینید این وضعیت نیازمند مقدار زیادی کار خسته‌کننده و تکراری است.

نشت حافظه

ما مهندسان غالباً از کارهای دشوار اجتناب می‌کنیم و این دقیقاً همان موقعیتی است که LeakCanary (+) به کمک ما می‌آید. این کتابخانه همراه با اپلیکیشن اجرا می‌شود و حافظه را در زمان‌های مورد نیاز dump می‌کند و به دنبال موارد احتمالی نشت حافظه می‌گردد تا به همراه یک رد پشته تمیز و مفید به شما اعلان کند و بدین ترتیب می‌توانید ریشه نشت حافظه را پیدا کنید. LeakCanary امکان تشخیص نشت حافظه را برای همه افراد به طور آسانی فراهم ساخته است.

برخی سناریوهای رایج نشت حافظه و روش اصلاح آن‌ها

برخی سناریوهای بسیار رایج نشت حافظه وجود دارند که بروز آن‌ها بسیار محتمل است و اغلب در تجربه توسعه روزمره اندروید با این سناریوها مواجه می‌شویم. زمانی که بدانید چه هنگام، کجا و چگونه نشت‌های حافظه رخ می‌دهند، می‌توانید بدون دشواری اقدام به اصلاح آن‌ها بکنید.

شنونده‌های ثبت نشده

موقعیت‌های زیادی وجود دارند که شما یک «شنونده» (Listener) را در اکتیویتی (یا فرگمان) خود ثبت می‌کنید، اما فراموش می‌کنید آن را از فهرست ثبت نام خارج کنید. در صورتی که خوش‌شانس نباشید، این وضعیت به سادگی منجر به نشت حافظه زیادی می‌شود. به طور کلی این شنونده‌ها همدیگر را متعادل می‌سازند و از این رو اگر جایی در اپلیکیشن ثبت شوند باید، آن‌ها را دقیقاً همان جا از فهرست ثبت خارج کنید.

اینک نوبت آن رسیده است که یک مثال ساده را بررسی کنیم. فرض کنید می‌خواهید به‌روزرسانی‌های مکان را در اپلیکیشن خود به دست آورید و از این رو باید سرویس سیستم LocationManager را استفاده کرده و یک شنونده برای به‌روزرسانی موقعیت ثبت کنید.

شما این اینترفیس شنونده را در اکتیویتی خود پیاده‌سازی می‌کنید و از این رو LocationManager یک ارجاع به آن نگهداری می‌کند. اکنون اگر زمان آن رسیده باشد که اکتیویتی متوقف شده و فریمورک اندروید متد ()onDestroy را روی آن فراخوانی کند، اما garbage collector نخواهد توانست وهله‌ای از آن را از حافظه پاک کند، زیرا LocationManager همچنان یک ارجاع قوی برای آن نگهداری می‌کند.

راه‌حل بسیار ساده است. کافی است شنونده را در متد ()onDestroy ثبت کنید و بدین ترتیب مشکل حل می‌شود. این جزییاتی است که اغلب ما فراموش می‌کنیم و یا شاید حتی نمی‌دانیم.

کلاس‌های داخلی

کلاس‌های داخلی در جاوا بسیار رایج هستند و به دلیل سادگی‌شان از سوی بسیاری از توسعه‌دهندگان اندروید برای وظایف مختلفی استفاده می‌شوند. اما اگر به صورت درستی از آن‌ها استفاده نمی‌کنید، همین کلاس‌های داخلی می‌توانند منجر به نشت‌های حافظه بالقوه‌ای شوند.

در ادامه به کمک یک مثال ساده به بررسی این وضعیت می‌پردازیم.

این یک اکتیویتی کاملاً ساده است که یک وظیفه با اجرای بلندمدت را در نخ پس زمینه آغاز می‌کند. این وظیفه می‌تواند یک کوئری پایگاه داده یا یک فراخوانی کُند شبکه باشد. پس از این که وظیفه پایان یابد، نتیجه در یک TextView نمایش می‌یابد. ظاهر همه چیز خوب است.

البته مشکلی وجود دارد. مشکل این است که در این کد کلاس داخلی غیر استاتیک یک ارجاع صریح به کلاس احاطه کننده بیرونی خود (که همان اکتیوتی است) نگهداری می‌کند. اکنون اگر صفحه را بچرخانید یا اگر وظیفه بلندمدت، بیشتر از عمر اکتیویتی طول بکشد، garbage collector نخواهد توانست وهله‌ای از یک اکتیوتی کامل را از حافظه پاک کند. بدین ترتیب یک خطای ساده منجر به نشت حافظه بزرگی می‌شود.

اما در این مورد نیز راه‌حل کار آسان است و کافی است نگاهی به کد زیر بیندازید تا آن را تشخیص دهید:

آن چنان که می‌بینید، ما کلاس داخلی غیر استاتیک را به یک کلاس داخلی استاتیک تبدیل کرده‌ایم، چون کلاس داخلی استاتیک هیچ ارجاع صریحی به کلاس محاط بیرونی خود نگه نمی‌دارد. اما اینک از یک کانتسکت استاتیک دیگر به متغیرهای غیر استاتیک (مانند TextView) در کلاس بیرونی دسترسی نداریم و از این رو مجبور هستیم ارجاع شیءهای مورد نیاز را از طریق سازنده به کلاس درونی ارسال کنیم.

قویاً توصیه می‌شود که این ارجاع‌های شیء را در یک WeakReference قرار دهیم تا از بروز نشت‌های حافظه بیشتر جلوگیری کنیم. بدین ترتیب شما باید با انواع مختلف ارجاع‌های موجود در جاوا آشنا باشید و بدانید که چگونه می‌توانید بهترین استفاده از آن‌ها را برای جلوگیری از بروز نشت حافظه داشته باشید.

کلاس‌های بی نام

کلاس‌های بی نام در میان اغلب توسعه‌دهندگان محبوب هستند، زیرا روشی که برای تعریف آن‌ها استفاده می‌شود موجب می‌شود که نوشتن کد با استفاده از آن‌ها بسیار آسان و فشرده باشد. اما برخی از توسعه دهنگان نیز بر این باور هستند که این کلاس‌های بی نام مهم‌ترین دلیل بروز نشت‌های حافظه هستند.

کلاس‌های بی نام چیزی به جز کلاس‌های داخلی غیر استاتیک نیستد که موجب بروز نشت‌های حافظه بالقوه می‌شوند و در مورد دلیل این مسئله در بخش قبلی توضیح دادیم. شما ممکن است در چند جای مختلف اپلیکیشن از آن‌ها استفاده کنید اما شاید این موضوع را ندانید که اگر روش استفاده از آن‌ها نادرست باشد می‌تواند تأثیر شدیدی بر روی عملکرد اپلیکیشن داشته باشد.

در کد فوق ما از یک کتابخانه بسیار محبوب به نام Retrofit (+) برای ایجاد یک فراخوانی شبکه و نمایش نتیجه در یک TextView استفاده می‌کنیم. کاملاً روشن است که این شیء قابل فراخوانی نیز یک ارجاع به کلاس اکتیویتی محاط خود نگه می‌دارد.

اکنون اگر فراخوانی شبکه روی یک اتصال بسیار کُند اجرا شود و پیش از فراخوانی پایان گیرد، اگر اکتیویتی به نوعی دچار چرخش یا تخریب شود، در این صورت کل وهله اکتیویتی نشت خواهد یافت.

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

Bitmap

هر تصویری که در اپلیکیشن می‌بینید چیزی به جز اشیای Bitmap نیست که شامل داده‌های پیکسلی یک تصویر است. این اشیای بیت‌مپ عموماً کاملاً سنگین هستند و اگر به درستی با آن‌ها برخورد نشود، منجر به نشت قابل توجهی در حافظه می‌شوند و می‌توانند در نهایت باعث از کار افتادن اپلیکیشن به دلیل خطای OutOfMemoryError شوند. حافظه بیت‌مپ مرتبط با منابع تصویر که در اپلیکیشن استفاده می‌کنید همیشه به صورت خودکار از سوی فریمورک مدیریت می‌شوند، اما اگر بیت‌مپ‌ها را به صورت دستی مدیریت کنید، باید مطمئن شوید که آن‌ها را پس از استفاده ()recycle می‌کنید.

همچنین باید بدانید که چگونه می‌توان بیت‌مپها را به درستی مدیریت کرد. بارگذاری بیت‌مپ‌های بزرگ به وسیله مقیاس‌بندی کردن آن‌ها و استفاده از کش کردن بیت‌مپ و pool کردن در موارد مقتضی موجب کاهش مصرف حافظه می‌شود.

Context

دلیل مهم دیگر بروز نشت حافظه سوءاستفاده از وهله‌های Context است. Context صرفاً یک کلاس مجرد است و کلاس‌های زیادی (مانند Activity, Application, Service و غیره) وجود دارند که آن را بسط می‌دهند تا کارکردهای خاص خود را ارائه دهند.

اگر می‌خواهید کارها را در اندروید انجام دهید، شیء Context بهترین همراه شما است. اما بین این Context-ها تفاوت وجود دارد. درک تفاوت بین Context سطح اکتیویتی و Context سطح اپلیکیشن و این که کدام یک در کدام شرایط استفاده می‌شود، بسیار حائز اهمیت است.

استفاده از Context اکتیویتی در مکان نادرست باعث می‌شود که ارجاعی به کل اکتیویتی نگهداری شود و موجب بروز نشت‌های حافظه بالقوه می‌شود.

سخن پایانی

اینک باید متوجه شده باشید که Garbage Collector چگونه کار می‌کند، نشت حافظه چیست و چگونه می‌تواند تأثیر عمده‌ای روی عملکرد اپلیکیشن داشته باشد. همچنین با روش تشخیص و اصلاح این نشت‌های حافظه آشنا شده‌اید. بنابراین دیگر عذر موجهی ندارید و از این پس باید شروع به ساخت اپلیکیشن‌های اندرویدی با کیفیت خوب و عملکرد بالا بکنید. تشخیص و اصلاح کردن نشت‌های حافظه نه تنها موجب می‌شود که تجربه کاربری بهتری پدید آید، بلکه شما را نیز به توسعه‌دهنده بهتری تبدیل می‌کند.

منبع: فرادرس


نشت حافظه در اپلیکیشن‌ های اندروید — از صفر تا صد

 برنامه نویسی  55 بازدید

ساخت یک اپلیکیشن اندروید کار آسانی است، اما ایجاد یک اپلیکیشن اندروید با کیفیت کاملاً بالا که از نظر حافظه کارآمد باشد چنین نیست. برخی توسعه‌دهندگان علاقه زیادی به تمرکز و یافتن قابلیت‌ها، کارکردها و اجزای UI جدید در اپلیکیشن‌های خود دارند. برخی برنامه‌نویسان تمایل دارند روی چیزی کار کنند که تأثیر دیداری بیشتری داشته باشد تا این که زمان خود را صرف چیزهایی بکنند که اغلب افراد در نگاه نخست ممکن است حتی متوجه آن نشوند. اما این رویه خوبی نیست، شما باید این عادت اولویت‌بندی پایین‌تر موارد مرتبط با بهینه‌سازی اپلیکیشن را ترک کنید. یکی از مهم‌ترین گام‌های بهینه‌سازی اپلیکیشن نیز اصلاح موارد نشت حافظه است.

این وضعیت به صورت طبیعی منجر به تشکیل یک بدهی فنی می‌شود که در بلندمدت، شروع به تأثیرگذاری روی کیفیت اپلیکیشن‌ها می‌کند. شما باید خود را عادت بدهید که به تدریج ذهنیت فوق را که اشاره کردیم کنار بگذارید و بیشتر روی عملکرد اپلیکیشن‌های خود متمرکز شوید.

مفهوم نشت حافظه برای بسیاری از توسعه‌دهندگان آزاردهنده است، چون آن را دشوار، زمان‌بر، خسته‌کننده و غیرضروری می‌پندارند، اما خوشبختانه هیچ کدام از این موارد صحت ندارند. زمانی که شروع به بررسی آن بکنید، متوجه می‌شوید که موضوع کاملاً زیبا و جذابی است.

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

Garbage Collector دوست شما است؛ اما نه همیشه

جاوا زبان پایداری است. در اندروید کدی به زبان‌های C یا ++C نوشته نمی‌شود (البته به جز برخی موارد نادر). بدین ترتیب مجبور نیستیم که کل فرایند تخصیص حافظه و آزادی سازی آن را شخصاً مدیریت کنیم. خوشبختانه جاوا به خوبی از عهده انجام این امور برمی‌آید.

پس اینک نخستین سؤالی که به ذهن می‌آید این است که آیا جاوا یک سیستم مدیریت حافظه اختصاصی درونی دارد که می‌تواند به صورت خودکار حافظه را در صورت استفاده نشدن پاک کند؟ در این صورت چرا ما به عنوان توسعه‌دهنده باید در مورد این موضوع دغدغه داشته باشیم؟ آیا Garbage Collector مستعد خطا است؟

پاسخ سؤال فوق منفی است. Garbage Collector دقیقاً همان طور که طراحی شده است کار می‌کند، اما این اشتباه‌های برنامه‌نویسی خود ما است که برخی اوقات Garbage Collector را از گردآوری بخش‌های استفاده نشده حافظه بازمی‌دارد.

بنابراین باید گفت که اساساً این خطای ما است که منجر به بروز مشکل در حافظه می‌شود. Garbage Collector یکی از برترین دستاوردهای جاوا محسوب می‌شود و از این رو شایسته احترام است.

توضیحی در خصوص Garbage Collector

پیش از آن که در خصوص موضوع مقاله بیشتر توضیح بدهیم، باید اندکی در مورد طرز کار عملی Garbage Collector بدانیم. تئوری کارکرد Garbage Collector کاملاً ساده است، اما آن چه در پشت صحنه اتفاق می‌افتد در پاره‌ای موارد کاملاً پیچیده است. در هر حال جای نگرانی نیست، چون ما به طور عمده روی بخش‌های ساده آن تمرکز خواهیم داشت.

نشت حافظه

هر اپلیکیشن اندروید (یا جاوا) باید یک نقطه آغازین داشته باشد که اشیا از آنجا شروع به وهله‌سازی می‌کنند و متدها فراخوانی می‌شوند. بنابراین می‌توانیم این نقطه آغازین را به عنوان ریشه یا root درخت حافظه تصور کنیم. برخی اشیا مستقیماً یک ارجاع به root دارند و برخی دیگر از آن‌ها وهله‌سازی می‌شوند و ارجاع خود را به این اشیا نگه می‌دارند و همین طور ادامه می‌یابد.

از این رو یک زنجیره از ارجاعات شکل می‌گیرد که درخت حافظه را تشکیل می‌دهند. بنابراین Garbage Collector از ریشه‌های GC کار خود را آغاز می‌کند و اشیا را مستقیماً پیمایش می‌کند یا به صورت غیرمستقیم تا root می‌پیماید. در انتهای این فرایند، برخی اشیا وجود خواهند داشت که هرگز از سوی GC ملاقات نشده‌اند.

این موارد همان Garbage (یا اشیای مرده) هستند و همین‌ها هستند که باید از سوی Garbage Collector دوست‌داشتنی ما گردآوری شوند. تا به اینجا داستان شبیه به یک افسانه ساده کودکان بوده است، اما در ادامه کمی عمیق‌تر می‌شویم تا با جذابیت واقعی کارکرد Garbage Collector آشنا شویم.

نشت حافظه چیست؟

تا به اینجا ایده خلاصه‌ای از ماهیت Garbage Collector و طرز کار عملی مدیریت حافظه در اپلیکیشن‌های اندرویدی به دست آورده‌ایم. اکنون نوبت آن رسیده است که روی موضوع نشت حافظه با جزییات بیشتری تمرکز کنیم.

به بیان ساده نشت حافظه زمانی رخ می‌دهد که یک شیء را مدت‌ها پس از آن که هدف خود را برآورده ساخت نگهداری کنیم. مفهوم واقعی موضوع به همین سادگی است. هر شیء طول عمر خاص خود را دارد که در زمان پایان یافتن، باید از آن خداحافظی کرد و در نتیجه حافظه را ترک می‌کند. اما اگر برخی شیء (-های) دیگر به صورت مستقیم یا غیرمستقیم این شیء را نگه دارند، در این صورت Garbage Collector نمی‌تواند آن را گردآوری کند. این وضعیت همان نشت حافظه است.

اما خبر خوب این است که لازم نیست در مورد وقوع رخداد منفرد نشت حافظه در اپلیکیشن نگران باشید، چون همه نشت‌های حافظه باعث از کار افتادن برنامه شما نمی‌شوند.

برخی نشت‌ها هستند که کاملاً کوچک محسوب می‌شوند (نشت چند کیلوبایت حافظه)، برخی از نشت‌ها نیز در خود فریمورک اندروید رخ می‌دهند که شما نمی‌توانید و نباید آن‌ها را رفع کنید. این موارد عموماً تأثیر اندکی روی عملکرد اپلیکیشن شما دارند و می‌توان آن‌ها را به صورت امنی نادیده گرفت.

اما موارد دیگری هستند که می‌توانند اپلیکیشن را از کار بیندازند و آن را به مقدار زیادی کُند سازند به ترتیبی که به زانو درآید. این نشت‌ها آن‌هایی هستند که باید مراقبشان باشیم.

چرا باید نشت‌های حافظه را رفع کرد؟

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

نشت حافظه

زمانی که کاربر به استفاده از اپلیکیشن شما ادامه می‌دهد، حافظه heap نیز شروع به افزایش می‌کند و اگر نشت حافظه در اپلیکیشن رخ بدهد، در این صورت حافظه استفاده نشده در heap نمی‌تواند از سوی GC رهاسازی شود. بنابراین حافظه heap در اپلیکیشن به صورت مداوم افزایش می‌یابد تا این که به نقطه مرگ می‌رسد و حافظه دیگری را نمی‌توان به اپلیکیشن تخصیص داد. در این مرحله خطای OutOfMemoryError اعلام شده و در نهایت اپلیکیشن از کار می‌افتد.

همچنین باید به خاطر داشته باشید که گردآوری garbage یک فرایند سنگین است و از این رو هر چه garbage collector کمتر اجرا شود، برای اپلیکیشن بهتر است.

زمانی که اپلیکیشن مورد استفاده قرار می‌گیرد و حافظه heap شروع به افزایش می‌کند، یک GC کوتاه اجرا می‌شود و شروع به پاکسازی بی‌درنگ شیءهای مرده می‌کند. در این مرحله این GC-ها به صورت همزمان (روی نخ مجزا) اجرا می‌شوند و اپلیکیشن شما را کُند نمی‌سازند و در کل یک مکث 2 تا 5 میلی‌ثانیه‌ای خواهد داشت.

اما اگر اپلیکیشن با نشت‌های حافظه جدی مواجه باشد که در پشت صحنه پنهان شده باشند، در این صورت GC-های کوتاه قادر به آزادسازی حافظه نخواهند بود و هیپ شروع به افزایش می‌کند، بدین ترتیب نیاز به یک GC بزرگ‌تر وجود خواهد داشت که عموماً موجب یک مکث از نوع «توقف کامل» (stop-the-world) در نخ اصلی اپلیکیشن می‌شوند. این مکث حدود 50 تا 100 میلی‌ثانیه زمان طول می‌کشد و بدین ترتیب باعث می‌شود که اپلیکیشن دچار وقفه جدی شود و برای مدت زمانی تقریباً غیر قابل استفاده شود.

بدین ترتیب اکنون تأثیر این نشت‌های حافظه را که بر روی اپلیکیشن واقع می‌شوند و همچنین علت این که چرا باید به سرعت آن‌ها را رفع کنیم را می‌دانیم. بدین ترتیب کاربران بهترین تجربه کاربری را که شایسته‌اش هستند به دست می‌آورند.

چگونه نشت حافظه را تشخیص دهیم؟

تا به اینجا احتمالاً به طور کامل قانع شده‌اید که باید این نشت‌های حافظه را که به صورت پنهان درون اپلیکیشن شما وجود دارند رفع کنید. اما سؤال این است که چگونه می‌توان آن‌ها را تشخیص داد؟

خبر خوب این است که اندروید استودیو ابزار بسیار مفید و قدرتمندی به این منظور دارد که Monitors نام دارد. در واقع مانیتورهای منفردی وجود دارند که نه تنها برای نظارت بر مصرف حافظه بلکه برای نظارت بر مصرف CPU و GPU نیز استفاده می‌شوند.

نشت حافظه

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

Allocation Tracker در مواردی که بتوانید از آن برای بررسی درصد حافظه تخصیص‌یافته به انواع شیءها در اپلیکیشن استفاده کنید بسیار کارآمد خواهد بود. بدین ترتیب می‌توان ایده روشنی از این که کدام اشیا بخش زیادی از حافظه را اشغال کرده‌اند و باید به طور خاص مورد رسیدگی قرار گیرند، به دست آورد.

اما این کار به تنهایی کافی نیست، چون اکنون باید از گزینه Dump Java Heap برای ایجاد یک heap dump استفاده کنید که در عمل نماینده یک اسنپ‌شات از حافظه در نقطه خاصی از زمان است. چنان که می‌بینید این وضعیت نیازمند مقدار زیادی کار خسته‌کننده و تکراری است.

نشت حافظه

ما مهندسان غالباً از کارهای دشوار اجتناب می‌کنیم و این دقیقاً همان موقعیتی است که LeakCanary (+) به کمک ما می‌آید. این کتابخانه همراه با اپلیکیشن اجرا می‌شود و حافظه را در زمان‌های مورد نیاز dump می‌کند و به دنبال موارد احتمالی نشت حافظه می‌گردد تا به همراه یک رد پشته تمیز و مفید به شما اعلان کند و بدین ترتیب می‌توانید ریشه نشت حافظه را پیدا کنید. LeakCanary امکان تشخیص نشت حافظه را برای همه افراد به طور آسانی فراهم ساخته است.

برخی سناریوهای رایج نشت حافظه و روش اصلاح آن‌ها

برخی سناریوهای بسیار رایج نشت حافظه وجود دارند که بروز آن‌ها بسیار محتمل است و اغلب در تجربه توسعه روزمره اندروید با این سناریوها مواجه می‌شویم. زمانی که بدانید چه هنگام، کجا و چگونه نشت‌های حافظه رخ می‌دهند، می‌توانید بدون دشواری اقدام به اصلاح آن‌ها بکنید.

شنونده‌های ثبت نشده

موقعیت‌های زیادی وجود دارند که شما یک «شنونده» (Listener) را در اکتیویتی (یا فرگمان) خود ثبت می‌کنید، اما فراموش می‌کنید آن را از فهرست ثبت نام خارج کنید. در صورتی که خوش‌شانس نباشید، این وضعیت به سادگی منجر به نشت حافظه زیادی می‌شود. به طور کلی این شنونده‌ها همدیگر را متعادل می‌سازند و از این رو اگر جایی در اپلیکیشن ثبت شوند باید، آن‌ها را دقیقاً همان جا از فهرست ثبت خارج کنید.

اینک نوبت آن رسیده است که یک مثال ساده را بررسی کنیم. فرض کنید می‌خواهید به‌روزرسانی‌های مکان را در اپلیکیشن خود به دست آورید و از این رو باید سرویس سیستم LocationManager را استفاده کرده و یک شنونده برای به‌روزرسانی موقعیت ثبت کنید.

شما این اینترفیس شنونده را در اکتیویتی خود پیاده‌سازی می‌کنید و از این رو LocationManager یک ارجاع به آن نگهداری می‌کند. اکنون اگر زمان آن رسیده باشد که اکتیویتی متوقف شده و فریمورک اندروید متد ()onDestroy را روی آن فراخوانی کند، اما garbage collector نخواهد توانست وهله‌ای از آن را از حافظه پاک کند، زیرا LocationManager همچنان یک ارجاع قوی برای آن نگهداری می‌کند.

راه‌حل بسیار ساده است. کافی است شنونده را در متد ()onDestroy ثبت کنید و بدین ترتیب مشکل حل می‌شود. این جزییاتی است که اغلب ما فراموش می‌کنیم و یا شاید حتی نمی‌دانیم.

کلاس‌های داخلی

کلاس‌های داخلی در جاوا بسیار رایج هستند و به دلیل سادگی‌شان از سوی بسیاری از توسعه‌دهندگان اندروید برای وظایف مختلفی استفاده می‌شوند. اما اگر به صورت درستی از آن‌ها استفاده نمی‌کنید، همین کلاس‌های داخلی می‌توانند منجر به نشت‌های حافظه بالقوه‌ای شوند.

در ادامه به کمک یک مثال ساده به بررسی این وضعیت می‌پردازیم.

این یک اکتیویتی کاملاً ساده است که یک وظیفه با اجرای بلندمدت را در نخ پس زمینه آغاز می‌کند. این وظیفه می‌تواند یک کوئری پایگاه داده یا یک فراخوانی کُند شبکه باشد. پس از این که وظیفه پایان یابد، نتیجه در یک TextView نمایش می‌یابد. ظاهر همه چیز خوب است.

البته مشکلی وجود دارد. مشکل این است که در این کد کلاس داخلی غیر استاتیک یک ارجاع صریح به کلاس احاطه کننده بیرونی خود (که همان اکتیوتی است) نگهداری می‌کند. اکنون اگر صفحه را بچرخانید یا اگر وظیفه بلندمدت، بیشتر از عمر اکتیویتی طول بکشد، garbage collector نخواهد توانست وهله‌ای از یک اکتیوتی کامل را از حافظه پاک کند. بدین ترتیب یک خطای ساده منجر به نشت حافظه بزرگی می‌شود.

اما در این مورد نیز راه‌حل کار آسان است و کافی است نگاهی به کد زیر بیندازید تا آن را تشخیص دهید:

آن چنان که می‌بینید، ما کلاس داخلی غیر استاتیک را به یک کلاس داخلی استاتیک تبدیل کرده‌ایم، چون کلاس داخلی استاتیک هیچ ارجاع صریحی به کلاس محاط بیرونی خود نگه نمی‌دارد. اما اینک از یک کانتسکت استاتیک دیگر به متغیرهای غیر استاتیک (مانند TextView) در کلاس بیرونی دسترسی نداریم و از این رو مجبور هستیم ارجاع شیءهای مورد نیاز را از طریق سازنده به کلاس درونی ارسال کنیم.

قویاً توصیه می‌شود که این ارجاع‌های شیء را در یک WeakReference قرار دهیم تا از بروز نشت‌های حافظه بیشتر جلوگیری کنیم. بدین ترتیب شما باید با انواع مختلف ارجاع‌های موجود در جاوا آشنا باشید و بدانید که چگونه می‌توانید بهترین استفاده از آن‌ها را برای جلوگیری از بروز نشت حافظه داشته باشید.

کلاس‌های بی نام

کلاس‌های بی نام در میان اغلب توسعه‌دهندگان محبوب هستند، زیرا روشی که برای تعریف آن‌ها استفاده می‌شود موجب می‌شود که نوشتن کد با استفاده از آن‌ها بسیار آسان و فشرده باشد. اما برخی از توسعه دهنگان نیز بر این باور هستند که این کلاس‌های بی نام مهم‌ترین دلیل بروز نشت‌های حافظه هستند.

کلاس‌های بی نام چیزی به جز کلاس‌های داخلی غیر استاتیک نیستد که موجب بروز نشت‌های حافظه بالقوه می‌شوند و در مورد دلیل این مسئله در بخش قبلی توضیح دادیم. شما ممکن است در چند جای مختلف اپلیکیشن از آن‌ها استفاده کنید اما شاید این موضوع را ندانید که اگر روش استفاده از آن‌ها نادرست باشد می‌تواند تأثیر شدیدی بر روی عملکرد اپلیکیشن داشته باشد.

در کد فوق ما از یک کتابخانه بسیار محبوب به نام Retrofit (+) برای ایجاد یک فراخوانی شبکه و نمایش نتیجه در یک TextView استفاده می‌کنیم. کاملاً روشن است که این شیء قابل فراخوانی نیز یک ارجاع به کلاس اکتیویتی محاط خود نگه می‌دارد.

اکنون اگر فراخوانی شبکه روی یک اتصال بسیار کُند اجرا شود و پیش از فراخوانی پایان گیرد، اگر اکتیویتی به نوعی دچار چرخش یا تخریب شود، در این صورت کل وهله اکتیویتی نشت خواهد یافت.

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

Bitmap

هر تصویری که در اپلیکیشن می‌بینید چیزی به جز اشیای Bitmap نیست که شامل داده‌های پیکسلی یک تصویر است. این اشیای بیت‌مپ عموماً کاملاً سنگین هستند و اگر به درستی با آن‌ها برخورد نشود، منجر به نشت قابل توجهی در حافظه می‌شوند و می‌توانند در نهایت باعث از کار افتادن اپلیکیشن به دلیل خطای OutOfMemoryError شوند. حافظه بیت‌مپ مرتبط با منابع تصویر که در اپلیکیشن استفاده می‌کنید همیشه به صورت خودکار از سوی فریمورک مدیریت می‌شوند، اما اگر بیت‌مپ‌ها را به صورت دستی مدیریت کنید، باید مطمئن شوید که آن‌ها را پس از استفاده ()recycle می‌کنید.

همچنین باید بدانید که چگونه می‌توان بیت‌مپها را به درستی مدیریت کرد. بارگذاری بیت‌مپ‌های بزرگ به وسیله مقیاس‌بندی کردن آن‌ها و استفاده از کش کردن بیت‌مپ و pool کردن در موارد مقتضی موجب کاهش مصرف حافظه می‌شود.

Context

دلیل مهم دیگر بروز نشت حافظه سوءاستفاده از وهله‌های Context است. Context صرفاً یک کلاس مجرد است و کلاس‌های زیادی (مانند Activity, Application, Service و غیره) وجود دارند که آن را بسط می‌دهند تا کارکردهای خاص خود را ارائه دهند.

اگر می‌خواهید کارها را در اندروید انجام دهید، شیء Context بهترین همراه شما است. اما بین این Context-ها تفاوت وجود دارد. درک تفاوت بین Context سطح اکتیویتی و Context سطح اپلیکیشن و این که کدام یک در کدام شرایط استفاده می‌شود، بسیار حائز اهمیت است.

استفاده از Context اکتیویتی در مکان نادرست باعث می‌شود که ارجاعی به کل اکتیویتی نگهداری شود و موجب بروز نشت‌های حافظه بالقوه می‌شود.

سخن پایانی

اینک باید متوجه شده باشید که Garbage Collector چگونه کار می‌کند، نشت حافظه چیست و چگونه می‌تواند تأثیر عمده‌ای روی عملکرد اپلیکیشن داشته باشد. همچنین با روش تشخیص و اصلاح این نشت‌های حافظه آشنا شده‌اید. بنابراین دیگر عذر موجهی ندارید و از این پس باید شروع به ساخت اپلیکیشن‌های اندرویدی با کیفیت خوب و عملکرد بالا بکنید. تشخیص و اصلاح کردن نشت‌های حافظه نه تنها موجب می‌شود که تجربه کاربری بهتری پدید آید، بلکه شما را نیز به توسعه‌دهنده بهتری تبدیل می‌کند.

منبع: فرادرس


طراحی شبکه تصاویر واکنش گرا با CSS Grid Layout — از صفر تا صد

شما به عنوان یک توسعه‌دهنده فرانت‌اند حتماً تاکنون تجربیاتی در زمینه CSS داشته‌اید، اما اغلب ما این روزها کار با CSS را از طریق فریمورک‌هایی مانند Bootstrap انجام می‌دهیم. برخی موقعیت‌ها وجود دارند که مجبور خواهیم بود مستقیماً با CSS کار کنیم و یکی از آن‌ها مواردی است که بخواهیم با سیستم Grid Layout کار کنیم. در این مقاله به طور عمده در مورد شیوه استفاده از CSS Grid Layout جهت ساختن یک شبکه از تصاویر صحبت خواهیم کرد.

ما قصد نداریم همه مشخصه‌های Grid Layout را بررسی کنیم، بلکه روی آن مشخصه‌هایی متمرکز می‌شویم که برای اجرای وظیفه فوق مورد نیاز هستند.

CSS Grid Layout به چه معنی است؟

Grid Layout در CSS یک سیستم طرح‌بندی دوبُعدی برای وب است. شبکه‌ها امکان سازماندهی محتوا در ردیف‌ها و ستون‌ها را به ما می‌دهند. طرح‌بندی یک صفحه وب با یک هدر، یک نوار کناری، ناحیه محتوای اصلی و یک فوتر (مانند تصویر 1 زیر) را تصور کنید. این اجزای صفحه وب نیازمند طرح‌بندی صحیحی روی صفحه هستند. Grid در CSS به ما کمک می‌کند که این کار را چنان که با بررسی یک مثال از شبکه تصاویر خواهیم دید، انجام دهیم.

CSS Grid Layout
تصویر 1: نمونه‌ای از Grid در CSS برای طرح‌بندی یک صفحه وب

در تصویر زیر شبکه تصاویری که از آن صحبت کردیم و می‌خواهیم بسازیم را مشاهده می‌کنید.

CSS Grid Layout

خوب هر چه تا اینجا در مورد تئوری صحبت کردیم کافی است. اینک نوبت کار عملی فرا رسیده است.

یک پوشه روی سیستم خود ایجاد کرد و نامی برای آن تعیین کنید. ما پوشه خودمان را Photogrid می‌نامیم. پوشه را در ویرایشگر متنی محبوب خود باز کنید. ما از VSCode استفاده می‌کنیم. 2 فایل ایجاد کنید که نام یکی index.html و دیگری main.css است. ما استایل های مورد نیاز را در فایل main.css می‌نویسیم. کد زیر را به فایل index.html کپی کنید:

همان طور که در قطعه کد فوق می‌بینید، 13 div ایجاد کرده‌ایم که هر کدام یک تصویر دارد و از سرویس عکس Unsplash واکشی می‌شود. div کانتینر کلاسی از نوع container. دارد. توجه کنید که برخی از فرزندان div کانتینر، دارای کلاس‌هایی مانند big ،.vertical. و horizontal. هستند. ما این div-ها را به طرز متفاوتی سبک‌بندی خواهیم کرد. اینک نوبت به استایل‌دهی شبکه تصاویر رسیده است.

ایجاد استایل برای شبکه تصاویر

در فایل main.css استایل‌هایی برای شبکه تصاویر خود ایجاد می‌کنیم و کار خود را با کلاس container آغاز می‌کنیم.

در ادامه به اختصار در مورد کارکرد هر یک از مشخصه‌های فوق صحبت می‌کنیم.

توضیح مشخصه‌های استایل

برای این که با هر کانتینر مانند یک کانتینر شبکه رفتار شود، باید نوع display به صورت grid و یا grid-inline برای شبکه‌های درون‌خطی تعریف شده باشد. مشخصه grid-template-columns به تعریف ستون‌هایی از کانتینر شبکه می‌پردازد. شما می‌توانید عرض ستون را با استفاده از یک کلیدواژه مانند auto-fit یا یک طول مانند 50px تعریف کنید. در مورد مثال فوق ما مقدار grid-template-columns را درون یک متد ()repeat تعریف می‌کنیم.

متد repeat نشان دهنده یک فرگمان تکراری از یک tracklist است. بنابراین یک مقدار مانند (repeat(3، 80px سه ستون ایجاد می‌کند که هر یک عرضی برابر با 80 پیکسل دارند. کلیدواژه auto-fit به مدیریت اندازه‌های ستون می‌پردازد. بدین ترتیب می‌توانیم بیشتری تعداد ممکن ستون‌ها را در ردیفی با طول مفروض قرار دهیم. برای نمونه یک مقدار grid-template-columns به صورت (repeat(auto-fit، 100px بیشترین تعداد ستون‌هایی که در div-های کانتینر شبکه وجود دارند با تنظیمات عرض 80 پیکسل تولید می‌کند. در نهایت تابع minmax به تعریف کمینه و بیشینه عرض هر ستون می‌پردازد. minmax برای ایجاد صفحه‌های واکنش‌گرا بسیار مفید است.

The grid-auto-rows اندازه یک ردیف شبکه را که به صورت صریح ایجاد شده است تعیین می‌کند. بنابراین بر اساس قطعه کد CSS فوق این بدان معنی است که هر div که در کانتینر شبکه داریم ارتفاعی برابر با 200 پیکسل خواهد داشت.

grid-gap اندازه فاصله بین ستون‌ها و ردیف‌ها را تعیین می‌کند. در مثال مورد بررسی، grid-gap آن مقدار 5 پیکسل هم برای فاصله بین ستون‌ها و هم بین ردیف‌ها است.

مشخصه grid-auto-flow به کنترل طرز کار الگوریتم auto-placement می‌پردازد و دقیقاً تعیین می‌کند که آیتم‌های با جایگذاری خودکار چگونه در شبکه جابجا می‌شوند. در مثال مورد بررسی، ما از الگوریتم بسته‌بندی dense استفاده کرده‌ایم که تلاش می‌کند آیتم‌های کوچکی را که در ادامه می‌آیند، ابتدا در جاهای خالی شبکه پر کند. کامنت کردن آن خط موجب بروز برخی فضاهای خالی در شبکه ما خواهد شد.

تکمیل کد شبکه تصاویر

همان طور که می‌بینید توضیح‌های فوق برای چند خط کد کمی زیاد محسوب می‌شوند، اما خوشبختانه شما اینک با فرایند کار به خوبی آشنا شده‌اید. اکنون باید پا را فراتر گذاشته و شبکه تصاویر را تکمیل کنیم. ابتدا باید مطمئن شویم که همه تصاویر به طور صحیحی در div-ها قرار می‌گیرند. به این منظور کد زیر را در فایل CSS پس از کلاس container. قرار دهید.

این کد به تعیین عرض و ارتفاع همه تصاویر در شبکه بر اساس 100% کانتینرهایشان می‌پردازد. در نهایت به استایل‌دهی div-ها با کلاس‌های verical ،.horizontal. و .big می‌پردازیم.

در این بخش به صحبت در مورد مشخصه‌های CSS در قطعه کد فوق می‌پردازیم.

مشخصه CSS به نام grid-column یک مشخصه اختصاری برای grid-column-start و grid-column-end است که اندازه شبکه و موقعیت درون شبکه را تعیین می‌کند. کلیدواژه span تعداد ردیف‌ها یا ستون‌هایی که یک grid-column یا grid-row باید پوشش دهد تعیین می‌کند.

در مثال فوق، برای این که طول برخی تصاویر دو برابر بزرگ‌تر باشد، مقدار grid-column را برای کلاس horizontal. برابر با span 2 و برای کلاس vertical. نیز برابر با span 2 تعیین می‌کنیم تا ارتفاع برخی تصاویر دو برابر از بقیه باشد. div-های دارای کلاس big. در هر دو گستره ردیف و ستون اندازه‌ای دو برابر معمول دارند. اینک فایل index.html را در یک مرورگر باز کنید و خروجی را مشاهده کنید.

اگر علاقه‌مند هستید کد کامل را بینید، می‌توانید به فایل‌های زیر رجوع کنید:

فایل index.html

فایل main.css

نسخه نهایی باید چیزی مانند تصویر زیر باشد:

CSS Grid Layout

توجه داشته باشید که CSS Grid Layout مشخصه‌های زیاد دیگری دارد که احتمالاً مورد توجه شما قرار خواهند گرفت. بنابراین می‌توانید بررسی آن را با مطالعه مستندات (+) آغاز کنید.

ا

منبع: فرادرس


گزاره break و continue در ++C — راهنمای کاربردی

دو گزاره به نام گزاره break و continue در زبان برنامه‌نویسی ++C وجود دارند که به طور خاص برای تغییر در گردش نرمال یک برنامه استفاده می‌شوند. در برخی موارد می‌خواهیم که اجرای یک حلقه برای یک شرایط تست خاص رد شود و یا بدون بررسی شرط حلقه بی‌درنگ خاتمه یابد. برای مطالعه بخش قبلی این سری مقالات آموزشی به لینک زیر مراجعه کنید:

حلقه while و do…while در ++C — راهنمای کاربردی

برای نمونه ممکن است بخواهیم روی داده‌های افرادی با سنین مختلف به جز سنین بالاتر از 65 حلقه‌ای تعریف کنیم. همچنین ممکن است بخواهیم نخستین فردی که 20 سال سن دارد را بیابیم. در چنین مواردی از گزاره‌های ;continue و ;break استفاده می‌کنیم.

گزاره break در ++C

گزاره break در ++C موجب خاتمه بی‌درنگ یک حلقه می‌شود. این حلقه می‌تواند هر نوعی از قبیل for ،while و do..while و همچنین گزاره‌ی switch شود.

ساختار break

در استفاده‌های عملی گزاره break تقریباً همواره درون بدنه یک گزاره شرطی یعنی if…else در حلقه استفاده می‌شود.

طرز کار گزاره break چگونه است؟

گزاره break

مثال 1: break در ++C

برنامه ++C برای افزودن همه اعداد وارد شده از سوی کاربر تا زمانی که کاربر عدد 0 وارد نماید:

خروجی

Enter a number: 4
Enter a number: 3.4
Enter a number: 6.7
Enter a number: -4.5
Enter a number: 0
Sum = 9.6

در برنامه فوق، عبارت تست همواره صحیح است. از کاربر تقاضا می‌شود که عدد دیگری را وارد کند هنگامی که کاربر مقدار 0 وارد می‌کند، عبارت تست درون گزاره if نادرست است و بدنه else اجرا می‌شود که موجب خاتمه حلقه می‌شود. در نهایت مجموع نمایش پیدا می‌کند.

گزاره continue در ++C

در برخی موارد ضروری است که از شرایط تست خاصی درون یک حلقه رد شویم. در چنین مواردی گزاره continue در زبان برنامه‌نویسی ++C استفاده می‌شود.

ساختار continue

در عمل گزاره ;continue تقریباً همیشه درون یک گزاره شرطی استفاده می‌شود.

کار با گزاره continue

گزاره break

مثال 2: گزاره continue در ++C

برنامه ++C برای نمایش عدد صحیح از 1 تا 10 به جز 6 و 9.

خروجی

1 2 3 4 5 7 8 10

در برنامه فوق، زمانی که i برابر با 6 یا 9 باشد، اجرای گزاره زیر درون حلقه با استفاده از گزاره ;Continue رد می‌شود:

cout << i << "\t";

بدین ترتیب به پایان بخش دیگری از مطالب راهنمای مفاهیم برنامه‌نویسی زبان ++C می‌پردازیم. برای مشاهده بخش بعدی این سری مطالب به لینک زیر رجوع کنید: