Въпрос
Ние разработваме персонализирана вдъхновена от EventEmitter система за съобщения в Objective-C . За слушателите да предоставят обратни извиквания, трябва ли да изискваме блокове или селектори и защо?
Кое бихте предпочели да използвате като разработчик, използващ библиотека на трета страна? Кое изглежда най-в съответствие с траекторията, насоките и практиките на Apple?
Заден план
Ние разработваме чисто нов iOS SDK в Objective-C, който други трети страни ще използват, за да вградят функционалност в своите приложения. Голяма част от нашия SDK ще изисква съобщаване на събития на слушателите.
Има пет модела, които знам за извършване на обратни извиквания в Objective-C, три от които не пасват:
- NSNotificationCenter - не може да се използва, защото не гарантира, че наблюдателите на поръчката ще бъдат уведомени и защото няма начин наблюдателите да попречат на други наблюдатели да получат събитието (както
stopPropagation()
би в JavaScript). - Наблюдение на ключ-стойност - не не изглежда като добра архитектура, тъй като това, което наистина имаме, е предаване на съобщения, не винаги обвързано със „състояние“.
- Делегати и източници на данни - в нашия случай обикновено ще има много слушатели, нито един, който с право може да се нарече делегат.
И две от които са претенденти:
- Селектори - при този модел , повикващите предоставят селектор и цел, които се извикват колективно за обработка на събитие.
- Блокове – въведени в iOS 4, блоковете позволяват функционалността да бъде предавана, без да е обвързана с обект като модела наблюдател/селектор.
Това може да изглежда като езотеричен въпрос за мнение, но смятам, че има обективен „правилен“ отговор, който просто съм твърде неопитен в Objective-C, за да определя. Ако има по-добър сайт на StackExchange за този въпрос, моля, помогнете ми, като го преместите там.
АКТУАЛИЗАЦИЯ #1 — април 2013 г
Избрахме блокове като средство за указване на обратни извиквания за нашите манипулатори на събития. До голяма степен сме доволни от този избор и не планираме да премахваме поддръжката на слушател, базиран на блокове. Той имаше два забележителни недостатъка: управление на паметта и импеданс на дизайна.
Управление на паметта
Блоковете се използват най-лесно в стека. Създаването на дълготрайни блокове чрез копирането им в купчината въвежда интересни проблеми с управлението на паметта.
Блокове, които извикват методи на съдържащия обект, имплицитно увеличават броя на препратките на self
. Да предположим, че имате сетер за свойството name
на вашия клас, ако извикате name = @"foo"
вътре в блок, компилаторът третира това като [self setName:@"foo"]
и запазва self
, така че да не бъде освободено, докато блокът все още е наоколо.
Внедряването на EventEmitter означава наличие на дълготрайни блокове. За да предотврати неявното запазване, потребителят на излъчвателя трябва да създаде __block
препратка към self
извън блока, напр.
__block *YourClass this = self;
[emitter on:@"eventName" callBlock:...
[this setName:@"foo"];...
}];
Единственият проблем с този подход е, че this
може да бъде освободен, преди манипулаторът да бъде извикан. Така че потребителите трябва да отменят регистрацията на своите слушатели, когато бъдат отменени.
Проектно съпротивление
Опитните разработчици на Objective-C очакват да взаимодействат с библиотеки, използвайки познати модели. Делегатите са изключително познат модел и затова каноничните разработчици очакват да го използват.
За щастие моделът на делегат и базираните на блок слушатели не се изключват взаимно. Въпреки че нашият излъчвател трябва да може да обработва слушатели от много места (наличието на един делегат няма да работи), ние все пак бихме могли да изложим интерфейс, който ще позволи на разработчиците да взаимодействат с излъчвателя, сякаш техният клас е делегат.
Все още не сме внедрили това, но вероятно ще го направим въз основа на заявки от потребители.
АКТУАЛИЗАЦИЯ #2 — октомври 2013 г
Вече не работя по проекта, който породи този въпрос, след като доста щастливо се върнах в родната си страна на JavaScript.
Интелигентните разработчици, които поеха този проект, решиха правилно да оттеглят изцяло нашия персонализиран базиран на блокове EventEmitter. Предстоящата версия премина към ReactiveCocoa.
Това им дава модел на сигнализиране от по-високо ниво от предоставяната преди това нашата библиотека EventEmitter и им позволява да капсулират състоянието вътре в манипулаторите на сигнали по-добре, отколкото нашите базирани на блокове манипулатори на събития или методи на ниво клас.