به نام خدا

بعد از چهار ماه گذشتن از قراردادن قسمت اول این آموزش تصمیم گرفتم قسمت دوم رو قرار بدم

یه مرور یه خطی به آموزش قبلی:

۰- هممممه ی تایپ ها اعم از Int, String, Array, Dictionary,Struct و... (به غیر از کلاس و کلاژر ) وقتی داخل یه متغییر دیگه ای assign میشن در واقع یه کپیشون توی رم ساخته میشه و اون متغییر به این مقدار جدید اشاره میکنه یعنی با تغییر دادن این متغییر جدید روی متغیر قبلی هیچ تغییری ایجاد نمیشه

۱.سویفت از مدیریت حافظه مرجع شماری استفاده میکنه

۲. دو تا از تایپ های سویفت به صورت مرجع استفاده میشن و مرجع شماری فقط برای این دو تا نوع استفاده میشه اولیش کلاس ها هستن و دومیش Clouser ها

۳. وقتی یک کلاس یا Clouser ساخته میشه و در یک متغییر assign میشه retain count اون آبجکت برابر ۱ میشه و هرچقدر این آبجکت رو در متغییر های دیگه assign کنیم یکی به این عدد اضافه میشه  و هرموقع متغییر ها scope شون تموم بشه یا خودمون برابر با nil قرارشون بدیم از این عدد کم میشه تا به صفر برسه و اون آبجکت ساخته شده از توی رم حذف بشه

۴. اگر یک کلاسی رو بسازیم و توی اون کلاس توی scope اصلی اون کلاس یک کلاس دیگه ای رو تعریف کنیم و بهش مقدار بدیم و براش کلاس فعلیمون رو بفرستیم یه retain cycle ساختیم که هیچوقت از توی رم حذف نمیشه (راه های هندل کردن چنین مشکلی رو توی این پست بررسی میکنیم)

 

خب بریم سراغ آموزش امروزمون:

برای اینکه بتونیم مشکل retain cycle رو حذف کنیم باید با دو کلمه کلیدی Weak و unowned آشنا بشیم.

گفتیم که اگر آبجکتی بسازیم به ازای هر assign کردن اون درون هر متغییری که میسازیم یه عدد به retain count اون آبجکت اضافه میکنیم اما میشه یه وقتایی توی یه متغییری assign کنیم ولی اون عدد زیاد نشه چجوری؟

کافیه اون متغییری که میخوایم توش رفرنسی از یه آبجکت رو قرار بدیم رو از نوع weak یا unowned بزاریم (تفاوت این دو رو خدمتتون پایین تر میگم)

توجه: چنانچه متغییری رو از نوع weak بسازید و توی اون یه کلاسی رو new کنید یا به اصلاح فنی تر instance از اون کلاس بسازید درجا در همون لحظه از توی رم حذف میشه و اون متغییر توی خط بعدی برابر با null میشه (چرا؟) چون آبجکت که ساخته میشه و توی این متغییر ریخته میشه retain countش برابر با ۱ نمیشه یعنی همون صفر میمونه پس درجا حذف میشه و اون متغییر nil میشه

خب کجاها باید بفهمیم که از weak استفاده کنیم؟

جاهایی که ممکنه retain cycle اتقاق بیفته (و یه جای دیگه که توضیحش اینجا درست نیست)

مثلا کلاس زیر رو ببینید که توش یه retain cycle اتفاق افتاده و هیچکدوم از آبجکت های ساخته شده حذف نمیشن از توی رم

class A {

    var b : B?

}

class B {
    var a : A?
    init () {
         a.b = self
    }
}

let myClass = B()

به همین سادگی یه فاجعه رخ میده

کلاس B ساخته میشه و retain Countش برابر ۱ میشه حالا داخل کلاس B یه کلاس A ساخته میشه که رفرنس خود B رو درون A میریزه حالا retain count آبجکتی که از B ساختیم برابر ۲ و retain Count آبجکت A برابر ۱ میشه حالا باید برای حذف شدن هرکدومشون منتظر بمونیم تا retain countش برابر ۰ بشه که هیچوقت نمیشه چون یا باید کلاس A حذف بشه تا یکی از عدد رفرنس B بیاد پایین یا B حذف بشه که اینکار به صورت خودکار اتفاق نمیفته دو تا راه داریم

۱. خودمون دستی یه جایی که میدونیم دیگه کلاس A رو توی کلاس B نیازش نداریم برابر nil قرار بدیم (حتی اگه a.b رو هم نیل کنیم کافیه)

۲. از کلمات کلیدی weak و unowned استفاده میکنیم

طبق روش دوم

weak var b : B?

 

از کجا بفهمیم که کدوم یک از متغییر ها رو باید weak کنیم؟

همیشه اونی رو که life time کمتری داره و اصلی نیست با متغییر های weak پیاده سازی کنید

مثلا یه ویوکنترلر دارید که توش یه کلاسی رو میسازید و خود ویوکنترلر رو به عنوان delegate به این آبجکتی که ساختید پاس میدید اینجا اون کلاس یه کلاس با تایم لایف کمتری نسبت به ویوکنترلر هست (یه جورایی ماکسیموم زمانی که میخوایم وجود داشته باشه تا زمانی هست که خود ویوکنترلر زنده باشه و اگر ویوکنترلر dismiss شده باشه دیگه نیازی باهاش نداریم) پس متغییر delegate رو که توی اون کلاس تعریف شده رو از نوع weak میگیریم 

 

یه خطر جدی راجع به Delegate:

هرموقع که قصد دارید کلاسی رو از بیرون کنترل کنید و براش delegate بسازید حتما توجه کنید که داخل اون کلاس متغییری رو که delegate رو ذخیره میکنه از نوع weak قرار بدید مگرنه یه retain cycle وحشتناک رو رقم زدید. 

توجه: حتی وقتی که یه TableView دارید و میخواید به سلول هاش یه delegate پاس بدید تا کنترل اکشن های داخلیشون رو از ویو کنترلر انجام بدید حتما توی سلول ها، Delegate رو به صورت weak نگه دارید مگرنه توی تمامی سلول ها شما یه refrence قوی از کلاس ویو کنترلر نگه داشتید که با بیرون اومدن از اون ویوکنترلر نه ویوکنترلر حذف میشه نه سلول ها.

برای اینکه بتونید یه delegate رو که عموما protocol هست از جنس weak کنید باید حتما پروتکل به شکل زیر باشه:

protocol MyCustomDelegate : class {

}

/// OR

protocol MyCustomDelegate : AnyObject {

}

موفق باشید

مطمئنا این تمام بحث نبود و بحث Closuer ها و Capture ها باقی موند که انشالله وقت بشه در یه جلسه دیگه توضیح میدم

موفق باشید

یاعلی