Мы разрабатываем пользовательскую EventEmitter, вдохновленнуюсистемой сообщений на Objective-C. Должны ли слушатели предоставлять обратные вызовы, должны ли мы требовать блоковили селекторови почему?
Что бы вы предпочли, как разработчик, использующий стороннюю библиотеку? Что кажется наиболее соответствующим траектории, руководящим принципам и практикам Apple?
Мы разрабатываем совершенно новый iOS SDK на языке Objective-C, который другие третьи стороны будут использовать для встраивания функций в свои приложения. Большая часть нашего SDK потребует передачи событий слушателям.
Я знаю пять шаблонов для выполнения обратных вызовов в Objective-C, три из которых не подходят:
stopPropagation()
в JavaScript).И два из них являются претендентами:
Это может показаться эзотерическим вопросом мнения, но я чувствую, что есть объективный «правильный» ответ, который я просто слишком неопытен в Objective-C, чтобы определить. Если для этого вопроса есть лучший сайт StackExchange, пожалуйста, помогите мне, переместив его туда.
Мы выбрали блокив качестве средства определения обратных вызовов для наших обработчиков событий. Мы в основном довольны этим выбором и не планируем удалять поддержку блочного прослушивателя. У него было два заметных недостатка: управление памятью и конструктивное сопротивление.
Блоки проще всего использовать в стеке. Создание долгоживущих блоков путем их копирования в кучу приводит к интересным проблемам управления памятью.
Блоки, которые вызывают методы содержащего объекта, неявно повышают счетчик ссылок 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 ожидают, что будут взаимодействовать с библиотеками, используя знакомые шаблоны. Делегаты — чрезвычайно знакомый шаблон, и поэтому канонические разработчики рассчитывают его использовать.
К счастью, шаблон делегата и прослушиватели на основе блоков не исключают друг друга. Хотя наш эмиттер должен иметь возможность обрабатывать слушателей из многих мест (наличие одного делегата не будет работать), мы все же можем предоставить интерфейс, который позволит разработчикам взаимодействовать с эмиттером, как если бы их класс был делегатом.
Мы еще не реализовали это, но, вероятно, сделаем это по запросам пользователей.
Я больше не работаю над проектом, породившим этот вопрос, и вполне счастливо вернулся на родину JavaScript.
Умные разработчики, взявшие на себя этот проект, решили полностью отказаться от использования нашего пользовательского блочного EventEmitter. Предстоящий выпуск переключился на ReactiveCocoa.
Это дает им шаблон сигнализации более высокого уровня, чем наша библиотека EventEmitter, ранее предоставленная, и позволяет им инкапсулировать состояние внутри обработчиков сигналов лучше, чем это делали наши блочные обработчики событий или методы уровня класса.