ملاحظةدقيقتا قراءة

مراقبات الخصائص تعطيك القيمة القديمة والجديدة أصلًا

يأخذ كلٌّ من willSet و‏didSet وسيطًا، ولكلٍّ منهما اسم افتراضي، فتسميته صراحةً زيادةٌ في الغالب. أما القواعد المتعلقة بمتى لا تعمل فأهمّ.

يستقبل كلٌّ من willSet و‏didSet القيمة الناقصة عنده: يأخذ willSet القيمة الداخلة، ويأخذ didSet القيمة السابقة. ولكلٍّ منهما اسم افتراضي، فهذا يُترجَم كما هو مكتوب:

var language = "ObjC" {
  willSet {
    print("about to replace \(language) with \(newValue)")
  }
  didSet {
    print("replaced \(oldValue) with \(language)")
  }
}

ويقرأ كل مراقب قيمةً من الوسيط والأخرى من الخاصية: ففي willSet تكون الخاصية ما زالت تحمل القيمة القديمة، وفي didSet تكون قد صارت تحمل الجديدة.

أما الصيغة الصريحة — willSet(newValue) و‏didSet(oldValue) — فتسمّي الوسيط باسمه الافتراضي نفسه، فلا تضيف شيئًا. وإنما تُكتب حين يكون اسم آخر أوضح:

didSet(previousUser) {
  guard previousUser.id != user.id else { return }
  reload()
}

ومتى لا تعمل هذه المراقبات أكثر إثارةً للالتباس من أسماء وسائطها. وفي ذلك ثلاث قواعد.

لا تعمل أثناء التهيئة

الإسناد إلى خاصية من داخل init لا يستدعي مراقباتها، بما في ذلك عبر الـ‏memberwise initializer. وما كان من الإعداد موضعه didSet فلا بدّ من استدعائه صراحةً في آخر init، وإلا لم يقع مع أول قيمة دون أن يظهر لذلك أثر.

لا تعمل عند الإسناد من داخل didSet

الكتابة إلى الخاصية داخل didSet الخاص بها لا تدخل المراقب من جديد، وهذا ما يمنع التكرار اللانهائي. فـ‏didSet الذي يحدّ قيمته أو يضبطها بنفسه يعمل، والذي كُتب على انتظار أن ترجع إليه كتابته لا يعمل.

تعمل مع كل كتابة، لا مع كل تغيير

إسناد القيمة نفسها إلى الخاصية يشغّل المراقبين. وإن كان المتن يستدعي إعادة تحميل أو إعادة تخطيط، ففحص التساوي هو المقصود عادةً:

didSet {
  guard oldValue != items else { return }
  tableView.reloadData()
}

والأمر نفسه يسري على المستوى الذي تحته: فإن كانت الخاصية تحمل struct، فتعديل أحد حقولها كتابةٌ إلى الخاصية كلّها، فـ‏settings.fontSize = 14 تشغّل didSet على settings — وهذا ممّا ينبغي معرفته قبل وضع عمل ثقيل في أحدها.

المواضيع