Преглед на собствеността и KVO в цел C

преглед

Здравейте. След неотдавнашен преход от обектив c към java, открих, че на Java липсват толкова добри неща като свойства и KVO (наблюдение на ключови стойности). Тази статия обсъжда защо по принцип е необходимо да се усложни достъпа до вътрешни променливи на даден обект, как се изпълнява цел в и каква полза имаме от гледна точка на проследяване на състоянието на обектите при използване на свойства и KVO.

Част 1 - собственост

Свойствата в цел c са приблизително променливи от публичен клас с модифицирани getters/setters. За да разберем по-добре защо е необходимо това, нека разгледаме един пример. Да приемем, че имаме компания клас, която съхранява броя на служителите на дадена компания (хора):

С мимолетен поглед забелязваме потенциален източник на грешки - броят на хората в компанията се декларира като int, включително отрицателни стойности. В този случай проблемът може да бъде отстранен чрез замяна на типа променлива с неподписан, но какво ще стане, ако искаме допълнително да стесним обхвата на валидните стойности. Да кажем, в съответствие с техническата спецификация, броят на служителите в една компания не може да надвишава хиляда.
Типично решение на този проблем в езици без вградена поддръжка за свойства е създаването на двойка аксесоар/мутатор:

Мутаторът ви позволява да промените стойността на променлива, като вземете предвид наложените ограничения, а присъствието на достъп е продиктувано от необходимостта да се скрие вътрешната променлива. В противен случай всеки може да се обърне директно към променливата и да напише грешен резултат там.
Изглежда, че проблемът е решен, но един факт си струва да се отбележи - използването на двойки аксесоар/мутатор прави кода много труден за четене. Сравнете кода за увеличаване на броя на работниците с един:

Очевидно във втория случай кодът е по-четлив и не е добре да принуждавате програмиста да напише двойка аксесоар/мутатор за всяка променлива. Цел C ви позволява да избегнете писането на този скучен код, като използвате свойства. Типично описание на клас, използващ свойства, изглежда така:

И при изпълнението на класа е достатъчно да добавите един ред, а останалото ще направи компилаторът.

След това можем да се позовем на нашата променлива чрез обичайната точка, но когато използваме свойства, е възможно да зададем редица допълнителни параметри, като:

Както можете да видите, броят на опциите е достатъчно голям, за да отговори на типичните нужди при писане на код. Ако има ограничения за изпратените стойности, трябва ръчно да замените зададения метод. Можете също така да замените ръчно метода get (който в някои случаи ви позволява да се отървете от ненужните променливи). Само не забравяйте, че в обект c функцията за достъп се записва без префикса get и синтеза ви позволява да посочите как да получите достъп до променлива в класа. Примерен код.

Ползите от използването на свойства са очевидни. Можем не само да се отървем от писането на ненужен код, но и да създадем псевдопроменливи, без да разкриваме как наистина работят.

В допълнение към опростяването на синтаксиса, използването на свойства ви позволява да използвате още няколко функции на езика, като например препращане към променлива чрез нейното име.

Или дори да напишете нова стойност там. И предвид възможността за получаване на списък с всички свойства точно по време на изпълнението на програмата, това отваря големи възможности. Имайте предвид, че по мое лично мнение е по-добре да не се използва такава възможност, защото това е по-скоро мръсен хак, отколкото типичен начин за достъп до променливи. Можете да прочетете повече за обективната c среда на изпълнение например тук.
Преди да говорим за KVO, струва си да споменем за известията в известията (известие)

Част 2 - уведомяване

Цел c ви позволява бързо и с минимални усилия да приложите модела на наблюдателя. Накратко, шаблонът на наблюдателя е модел на дизайн, при който има два обекта - наблюдателят и наблюдаваният. Наблюдателят се интересува от събитията, които се случват с наблюдаваното, но вторият наблюдаван флегматично протича с потока, като от време на време изпраща известия за случващите се с него събития. Гледаният дори не се интересува, ако някой го чуе, но продължава да изпраща известия. Този подход ви позволява да приложите обратна връзка към наблюдаваните промени.
Цел c има много усъвършенстван механизъм за работа с известия, така че един ред код е достатъчен, за да регистрира наблюдател:

Където обект е обектът, който ще бъде наблюдателят, сложната конструкция @selector (obserNotification:) показва кой метод трябва да бъде извикан на наблюдателя (в този случай наблюдайтеNotification:) и nameNotification е името на събитието, което ще гледаме. В последния параметър можете да посочите от кой обект очакваме да получим известие или просто да получим всички известия с дадено име, което е удобно, ако не се знае предварително кой ще изпрати известието.
В класа на наблюдателя трябва да приложите горния метод (obserNotification:), а в обекта, който изпраща известия, е достатъчно да напишете:

И тук свършва кодът, необходим за най-простото изпълнение на шаблона. Както можете да видите, не се наложи да напишем никакъв код, който да съхранява връзките между наблюдателя и наблюдавания, но досега имаше нужда от промяна на кода на наблюдаваното. Но KVO ви позволява да избегнете ненужна намеса в чужд код.

Част 3 - KVO

KVO (наблюдение ключ-стойност) е друго изпълнение на модела на наблюдател, дори по-интересно от предишното. Ако в предишната версия наблюдателят е наблюдавал абстрактни събития, знаейки само името, в KVO обхватът е донякъде стеснен. В този случай наблюдателят ясно знае обекта, който наблюдава и какво свойство на обекта наблюдава. Когато стойността на това свойство се промени, наблюдателят получава известие и те реагират съответно.
В сравнение с много други езици, прилагането на KVO в цел c е приятно с доста прост синтаксис. Така че в кода на наблюдателя е достатъчно да напишете:

и всеки път, когато стойността на променливата people в company_a се промени, наблюдателят ще бъде уведомен, като извика метода obserValueForKeyPath: (NSString *) keyPath ofObject: (id) промяна на обекта: (NSDictionary *) промяна на контекста: (void *) контекст и просто трябва да приложите кода, който ще отговори на известието.
Вероятно сте искали да попитате, какво трябва да се добави в кода на класа на обекта, който се наблюдава (в случая компания), за да се приложи такава прекрасна функция - така че ако използвате свойства с методите get/set, автоматично синтезирани от @ synthesize директива, тогава не е нужно да добавяте нищо ... В противен случай кодът, който променя вътрешната променлива, трябва да бъде заобиколен от две помощни функции (willChangeValueForKey: и didChangeValueForKey:). Така че използваният по-рано пример трябва да бъде модифициран, както следва:

  • Минимализъм на кода. (достатъчно е да напишете само няколко реда, за да приложите напълно модела на наблюдателя)
  • Възможността за проследяване на всякакви свойства (свойства) на всякакви класове, както написани от нас, така и от други. Всъщност външните променливи винаги са стилизирани чрез свойства, което улеснява проследяването на всякакви промени.
  • Повтарям възможността да проследявам всякакви свойства на всякакви класове, било то състояние в NSOperation или кадър в UIView. KVO ви позволява да наблюдавате промените в параметрите от всеки клас
  • И отново, няма значение кой е написал класа, дали неговият изходен код е там или не. Ще имате възможност да следите промените и това е просто фантастично нещо, което улеснява много писането на код за реакции на събития.

Всъщност една и съща заслуга се споменава три пъти по-горе, но това е само защото няма аналози на такъв мощен механизъм в други популярни езици. Ако искате да реагирате по някакъв начин на промяна в един от параметрите, или трябва да влезете вътре и да пренапишете кода на обекта (което в повечето случаи просто не е възможно) или да измислите други, дори по-сложни решения. KVO, от друга страна, ви позволява да разрешите този проблем бързо и с минимално количество труд, което прави KVO една от основните характеристики на цел C.