Селекторы или блоки для обратных вызовов в библиотеке Objective-C

Вопрос

Мы разрабатываем пользовательскую 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, ранее предоставленная, и позволяет им инкапсулировать состояние внутри обработчиков сигналов лучше, чем это делали наши блочные обработчики событий или методы уровня класса.

19
задан jimbo 5 October 2013 в 21:33
поделиться