Защо се нуждаем от Delegate в iOS и watchOS

Преди около две години някой ми зададе добър въпрос: „Защо имаме нужда от делегати заUIViewControllers?“ Той смяташе, че Суифт прави много неща по-лесни, но цялото това делегатство изглежда много сложно. Защо не можете просто да изпращате съобщения или инициализации между класовете?

Когато за първи път научих iOS, признах, че ми отне месеци, за да разбера какво се е случило с делегацията. Намерих много неясен код и някои обяснения. Когато работих върху него, резултатът не беше достатъчен. В повечето случаи уроците се отнасят до информация как да използвате стандартния делегат на Apple, но не показват как да създадете свой собствен отговор. Тези отговори са необходими за пълното разбиране на делегатите.

Реших, че е време да актуализирам статията, за да включва два примера, на които разработчиците могат да попаднат: версиите за iOS и watchOS. Тъй като watchOS узрява в watchOS 3, мисля, че много разработчици ще започнат да търсят разработка на приложения за часовници и може да се случват някои странни неща.

Какво е клас?

Да започнем отначало, за да разберат всички проблема. Докато използваме класове в обектно-ориентираното програмиране, е добре да разберем какво представляват те.Класе колекция от данни, които наричамесвойства(свойства) и действияметоди(методи) досвойства.Свойстватаиметодитемогат да бъдатпублични(публични) иличастни(частни).Публиченметодвижда и използва класове, различни от дефиниращия.Частенозначава, че свойствата или методите са видими и използвани в рамките на дефиниращия клас, а другите класове не могат да ги виждат или използват. В Swiftprivateправисвойстваиметодичастен. Изчисляването насвойствае друг начин да направите свойствата лични. Освен това Swift има състояние по подразбиране, което правиmethodилиclassпублични само за текущата цел.

Много разработчици не обичат да си играят с техния код, което също е добро програмно изживяване. Като цяло си струва да се стремим да запазим нужния минимум за други класове публичен. Запазването насвойстватаиметодитечастни и скриването на класове се наричакапсулиране(капсулиране).

Капсулирането ви позволява да изграждате код, сякаш строим къща от блокове. Точно както обикновената тухла има множество приложения, един клас има множествометодиприложения. След това те могат да прикрепят много други тухли.

Какво е модел-изглед-контролер или MVC?

защо

Какво е модел?

watchos

Има части от програмата, които работят с данните, които искаме да обработим. Системата за поръчка на пица разполага с данни за всяка поръчка, които могат да съдържат връзки към по-подробни данни за всеки клиент и пица. Това е нашиятМоделв системата за поръчка на пица: колекция от всички данни, използвани в системата за поръчка. Не трябва да контактува с потребителя по никакъв начин, не иска нищо и не го показва. Това са само данни. Ето пример за прост модел:

Този модел на състояние на превключване. Това са данни. Моделът има дваметода: textState() - описва състоянието на превключвателя катоString, overLoadSwitch() изключва превключвателя, ако броят потребители * натоварване е по-голям от 100. Има още много методи, които трябва да добавя, за да опиша превключвателя, но всички методи променят или описват само данни. Няма вход отпотребител.Моделътможе да прави изчисленията, но отново няма взаимодействие с потребителя.

Какво е View?

delegate
защо

Изгледъте мястото, където се извършват всички потребителски взаимодействия. Повечето хора в Xcode използватInterface Builderкато платно за използване наstoryboardили .xibза изграждане наView. Разработчикът може програмно да създаде класView, който съдържа различни контроли. Точно кактоModelникога не взаимодейства с потребителя,Viewникога не взаимодейства директно с данните.Изгледътне прави нищо, той просто е такъв. Може да реагира на докосвания на потребителя, като сигнализиране за някакъв метод, промяна на цвета при натискане на бутон или превъртане през списък, но това е всичко, което прави.Viewсъдържа много свойства и методи и ни казва за състоянието наView. Можем да променим поведението на интерфейс или външния му вид чрез методи или свойства.Изгледътможе да сигнализира наКонтролера, че е имало промени вИзгледа, като например натискане на бутон или въвеждане на знак. Но той не може да направи нищо по въпроса, освен да предаде информацията.

Какво е контролер?

delegate

Сърцето на MVC съчетаваИзгледиМодел. НареченControllerилиViewController, той координира какво се случва в данните илиView. Ако потребителят щракне върху бутона,Контролерътотговаря на това събитие. Ако този отговор означава изпращане на съобщениеModel,ViewControllerго прави. Ако отговорът изисква изход отModel,Controllerправи и това. В Xcode @IBOutlet и @IBAction обединяват файлове, съдържащи изгледи в Interface Builder иViewController.

Ключът към MVC е комуникацията. За да бъдем точни, тянедостатък. MVC приема капсулирането много сериозно.ИзгледиМоделникога не комуникират директно един с друг.Controllerможе да изпраща съобщения доViewиController.ИзгледиКонтролермогат да извършват вътрешни действия в отговор на съобщения, като извикване на метод, или могат да връщат стойност наКонтролер.Контролерътникога не прави промени директно нито вМодела, нито вИзгледа.

ТакаИзглед,МоделиКонтролерне променят взаимно свойствата си.ИзгледиМоделможе изобщо да не комуникират помежду си.Изгледможе да каже наКонтролер, че има промяна вМодел.Контролерътможе да изпраща съобщения под формата на извиквания на метод къмИзглед,Модели да получава отговори чрез този метод.

нуждаем

Как MVC комуникира с други MVC

Всичко, което обсъждахме досега, е само за една сцена в доста голямо приложение. Да кажем, че имам разкадровка на watchOS като тази:

watchos

Има бутон за превключване, който зарежда другИзглед, който има превключвател. След като реша къде трябва да бъде радиобутонът, ще натиснаГотово.

Лесен начин

В Xcode имаме сеги (сегуей). Segways са удобни за насочване от единViewControllerкъм друг. Когато Segway улеснява нещата за нас и преминаваме от единконтролеркъм друг, Segway казва на системата да отвори конкретенViewControllerи след това отваряViewиModel.МоделиИзгледв новата настройка на MVC са различни от повикващия. Apple е включил метод за подготовка (за преминаване:) за iOS, който ни дава възможност да зададем стойности в новияViewControllerи съответно вViewControllerнаViewиМодел.

С появата на WatchOS се появи малко по-различен подход. Вместо достъп до пълнияМодел, watchOS изпраща една стойност, нареченаконтексткъм новите контролери. Тъй като е от типAny?, можете да му подадете произволна стойност. Най-често разработчиците предават стойности наречницина други контролери чрез метода contextForSegue.

За идентични приложения за iPhone и часовник мога да натисна бутона за превключване. Той стартира интерфейса за превключване и предава стойността на превключвателяfalse.

Проблемен път

Можем да включваме и изключваме превключвателя доста лесно, но когато натиснем Готово, за да го изпратим обратно към оригиналния контролер, там идва проблемът. Според правилата на MVC се нуждаем от метод, който връща стойност. Къде в извиканияинстанцияможем да се върнем към неговия извикващ клас?

С капсулиран клас не можем. В тази ситуация няма начин да изпратите ревизиранияМоделобратно към оригиналния контролер, без да нарушите капсулирането или MVC. НовиятViewControllerне знае нищо за класа, който го е извикал. Изглежда, че сме заседнали. Ако се опитаме да създадем препратка директно към извикващия контролер, ще извикамереферентен цикъл, което ще убие паметта. Просто казано, не можем да изпратим данни обратно.

Това е проблем, който делегатите и протоколите решават, като са малко пъргави. Представете си друг клас, който всъщност е скелетът на един клас. Този клас съдържа само методи. Той декларира определени методи в този клас, но никога не ги използва. В Swift те се наричат ​​протоколи. Ние създаваме протокол, съдържащ един метод. Този метод е това, което правите, когато приключите с превключвателя и искате да се върнете към негоизвикващият контролер. Той има няколко параметъра, данни за връщане към извикващия контролер. Може да изглежда така:

В този случай предадох обратно стойността на превключвателя.

В контролера на комутатора създавамеекземплярна този протокол и го наричаме делегат.

Тъй като имаме свойство от тип SwitchDelegate, можем да използваме методи от тип SwitchDelegate, като например метода didFinishSwitch. Можем да обвържем изпълнението на този метод с бутона Готово:

или за watchOS:

След като направите това, ще получите грешка при компилиране, защото методът на протокола не съществува в класа. В кода, за да адаптираме класа към примера OrderPizzaViewController, използваме метода:

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

Още една стъпка. По време на връщането към присвоения контролер казах, че делегатът е протоколът наinstanc, но не казах къде е делегатът. В подготви (за Segue:) добавих допълнителен ред vc.delegate = самостоятелно съобщаване на протокола за вашия контролер:

В WatchOS нещата са малко по-различни. Имам само единконтекст, предаден на switchState и делегат. За множество стойности разработчиците обикновено използватречникпо този начин:

Методътawakeима код за извличане наречники присвояване на стойности. Когато щракнете върху Готово на часовника или в приложението, методът се изпълнява, той знае какво има в извикващия контролер и ще бъде извикан там, където сме го добавили към оригиналния клас.данние параметър, който програмата може лесно да прехвърли към контролера и къмМодела. Делегатите и протоколите са доста трудни, но работят ие една от най-важните техники при работа сViewControllers.

методи

Основният въпрос беше защо Swift не направи всичко по-лесно. Тъй като показах всичко тук в контекста на Swift, няма значение кой обектно-ориентиран език използвате. Делегирането е част от MVC последователността и е основно умение за програмиране.

Можете да помогнете и да прехвърлите средства за развитието на сайта