بسم الله الرحمن الرحیم

شاید با نصب یه آیدی و ران کردن یه اپلیکیشن روی گوشیمون خیلیامون فکر کنیم که حالا یه برنامه نویس شدیم. قضیه اونجایی بدتر میشه که یه اپلیکیشن بزرگ بزنیم بعد اینکه یه اپلیکیشن نسبتا بزرگی رو زدیم (مثلا حس کردیم برنامه های بزرگی که الان موجود هستن و نصب چندده میلیونی دارن رو میتونیم با دانش فعلیمون پیاده کنیم) حس گندگی میکنیم و دیگه خدا رو بنده نیستیم اون موقع به خودمون میگیم God Of iOS.

درحالی که راه برای تبدیل شدن به یه برنامه نویس حرفه ای Senior زیاده. از چیزایی که من شخصا بهشون رسیدم چیزایی مثل تست نوشتن ها، معماری ها، تسلط بر دیزاین پترن ها مخصوصا DI و چیزی که امروز میخوایم درس بدیم یعنی دانش مدیریت حافظه توی پلتفرمی که براش کد میزنید.

شاید تا حالا مدیریت حافظه بر روش Garbage collection رو شنیده باشید (به اختصار GC) توی این آموزش قصد بررسی این مدیریت حافظه رو نداریم‌ (منم در حد توضیح دادنش نیستم) اما خیلی از زبان ها مثل java و c++ و خیلیای دیگه از این نوع مدیریت حافظه بهره میبرن.

اما اپل GC رو برای آی او اس نپسندید و ترجیح داد که سیستم Refrence Counting (به فارسی مرجع شماری) رو پیاده کنه.

برای روشن شدن مطلب بهتره اول این سوالو برای خودتون جواب بدید که وقتی یه تابع دارید و یه استرینگ یا اینتیجر رو براش میفرستید آیا اون استرینگ و یا اون اینتیجر رو میتونید از توی تابع تغییر بدید و از بیرون تابع یعنی اون متغییر اصلی که پاس کردید رو تغییر بدید؟

جواب خیر هست، چون اون چیزی که شما داخل تابع دریافت میکنید یه متغییر جدیدیه که مقدار اون متغییری که به تابع پاس کردید توش ذخیره شده پس به اون متغیر بیرونی که پاس دادید به داخل تابع دسترسی ندارید و قابل تغییر نیست براتون.

استثنا: کلاس ها از این قاعده مثتسنا هستند یعنی هممممه ی تایپ ها اعم از string, array, list, float, int ,enum و حتی استراکت bypass value یا همون value type هستن اما کلاس ها همشون refrence type هستند و وقتی داخل یه تابع پاس داده میشن مقدارشون کپی نمیشه بلکه رفرنس هستند و ارجاع داده میشند به همون کلاسی که پاس داده شده.

نکته ای بس مهم و ارزشمند: در سویفت closure ها نیز مانند کلاس ها refrence type هستند.

closure ها همان {} هایی هستند که در متغییر ها ذخیره می شوند یا مستقیما در داخل توابع ارجاع داده میشوند.

پس فهمیدم اگه ما یه کلاسی رو به یه تابع فرستادیم و متغییر درون کلاس رو تغییر دادیم، متغییر کلاسی که پاس داده شده و کدش قبل کال کردن تابع نوشته شده هم تغییر کرده.

لازم به توضیحه که توی زبان سویفت ما هم Class داریم هم Struct که توی struct ها همیشه باید حالت ذخیره سازی دیتا رو رعایت کنیم نه بیشتر، یعنی استراکت ها رو زمانی استفاده کنید که نیاز دارید یه سری اطلاعات رو ذخیره کنید مثل:

struct Resolution {
    var width = 0
    var height = 0
}

خب تفاوت اصلیش اینجاست که وقتی شما اینو مقداری دهی میکنید و به یه تابعی پاس میدید،‌ کل این استراکتی که مقدار دهی کردید کپی میشه و میره توی تابع مربوطه که به این ویژگی میگن bypass value .

خب بریم ادامه آموزش خودمون

مرجع شماری چیست و چگونه عمل میکند؟

وقتی شما از یک کلاس یک شئ میسازید برای اسم اون مرجع یا رفرنس یک شمارنده ای ایجاد میشه که به ازای هر پاس داده شدن به داخل تابع و همچنین مساوی قرار گرفتن یه متغییر از همین جنس با همین رفرنس یه عدد زیاد میشه و با حذف شدن متغییر یا تموم شدن تابع یکی از همون retain count کم میشه (البته بعدا توضیح میدیم که این کم و اضافه شدن فقط برای متغییر های strong هستن).

هرموقع که retain count یه رفرنس به ۰ برسه اون رفرنس از توی رم حذف میشه.

هرموقع شما درون یک تابع از یه کلاسی یه آبجکت ساختید و داخل یه متغییری ریختید وقتی اون تابع تموم بشه، تابع deinit اون کلاسی که توی تابعتون instance کردید صدا زده میشه و اینجوری مقدار retain count اون رفرنس یکی کم میشه (یعنی صفر میشه) و حذف میشه.

فرض کنید یه متغییر گلوبال داخل کلاستون دارید که از توی یه تابع یه کلاسی رو instance میکنید و مقدار اون متغییر گلوبال رو هم با مقدار این متغییری که داخل تابع ساختید و توش کلاس رو instance کردید برابر قرار میدید (یعنی assign میکنید) حالا refrence counting اون رفرنس شما برابر ۲ میشه که با تموم شدن تابع این مقدار به ۱ تغییر میکنه اما حذف نمیشه تا زمانی که کل کلاستون حذف بشه (یعنی هرجایی که از کلاستون شئ ساخته شده و داره استفاده میشه تموم بشه و حالا از توی رم حذف بشه)

وقتی درون یه تابع چندتا متغیر ایجاد میکنیم میدونیم که وقتی تابع تموم بشه متغیر ها از بالا به پایین از توی استک برداشته میشن و حذف میشن. یه قانونی کشف کردم که فکر کنم بین متغیر هایی که توشون کلاس instance کردید اولیت حذف با اونایی که retain count رفرنسشون عدد کمتری رو داره :)

نکته: ARC هیچ کاری با استراکت ها نداره و استراکت ها مثل int و float و... بعد اینکه کلاس تموم شد حذف میشن یعنی چون با پاس دادن رفرنسشون ارجاع داده نمیشه و کپی میشن پس حساسیت کلاس رو ندارن و بعد از اینکه به آکولاد بسته ای که توش هستن برسن حذف میشن

خب تا اینجا که ساده بود. حالا یکم بحث رو پیچیده ترش میکنیم.

فکر کنید که دو تا کلاس به اسم A و ‌B داریم و توی کلاسی به اسم C  در داخل یکی از توابع کلاس داریم از این دوتا کلاس A و B شئ میسازیم.

حالا فکر کنید که این دوتا رو بهم وصل کردیم یعنی ابجکتی که از A ساختیم رو بریزم داخل b و بلعکس

یه فاجعه: باکاری که کردیم الان یه strong refrence cycle داریم که همیشه این دوتا رفرنس رو زنده نگه میداره و  ARC نمیتونه پاکش کنه.

راه درست کردنش اینه که یکی از متغییر ها رو توی کلاس A و یا B تبدیل به weak و یا unowned کنیم که توضیحاتشو انشالله توی یه پست دیگه مفصل براتون میگم.

شبتون بخیر

یاعلی