objective-c - чайников - потоки objective c




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

Лично я ненавижу использовать делегатов. Из-за того, как объективно-C структурирован, он действительно загромождает код. Если мне нужно создать отдельный объект / добавить протокол, просто чтобы получить уведомление об одном из ваших событий, и я должен реализовать 5/6. По этой причине я предпочитаю блоки.

Хотя они (блоки) имеют свои недостатки (управление памятью ex может быть сложным). Они легко расширяемы, просты в реализации и имеют смысл в большинстве ситуаций.

Хотя в структурах дизайна Apple может использоваться метод sender-Delegate, это только для обратной совместимости. Более поздние API Apple использовали блоки (бывшие CoreData), потому что они - будущее цели c. Хотя они могут загромождать код при использовании за бортом, он также допускает более простые «анонимные делегаты», что невозможно в задаче C.

В конце концов, все сводится к следующему: готовы ли вы отказаться от более старых, устаревших платформ в обмен на использование блоков и делегата? Одним из основных преимуществ делегата является то, что он гарантированно работает в любой версии objc-runtime, тогда как блоки являются более поздним дополнением к языку.

Что касается NSNotificationCenter / NSNotificationCenter , они оба полезны и имеют свои цели, но как делегат не предназначены для использования. Ни один из них не может отправить результат обратно отправителю, а для некоторых ситуаций это жизненно важно (например, -webView:shouldLoadRequest: .

Вопрос

Мы разрабатываем пользовательскую систему сообщений, вдохновленную EventEmitter , в Objective-C. Для слушателей, чтобы обеспечить обратные вызовы, мы должны требовать blocks или selectors и почему?

Что бы вы предпочли использовать в качестве разработчика, потребляющего стороннюю библиотеку? Что кажется наиболее соответствующим траектории, руководящим принципам и практикам Apple?

Фон

Мы разрабатываем новый iOS SDK в Objective-C, который будут использовать другие третьи стороны для встраивания функциональности в свое приложение. Большая часть нашего SDK потребует сообщения событий слушателям.

Есть пять шаблонов для выполнения обратных вызовов в Objective-C, три из которых не подходят:

  • NSNotificationCenter - не может использоваться, потому что он не гарантирует, что наблюдатели будут уведомлены о заказе, а также потому, что у наблюдателей нет возможности предотвратить получение события другими наблюдателями (как stopPropagation() в JavaScript).
  • Наблюдение значения ключа - не похоже на хорошую архитектурную подгонку, поскольку на самом деле мы имеем передачу сообщений, а не всегда привязку к состоянию.
  • Делегаты и источники данных - в нашем случае обычно будет много слушателей, а не один, который по праву можно назвать делегатом.

И два из которых, которые являются претендентами:

  • selectors - в этой модели вызывающие абоненты предоставляют селектор и цель, которые совместно вызываются для обработки события.
  • blocks - представленные в iOS 4, блоки позволяют передавать функциональность без привязки к объекту, подобному шаблону наблюдателя / селектора.

Это может показаться эзотерическим вопросом, но я чувствую, что есть объективный «правильный» ответ, который я просто слишком неопытен в Objective-C, чтобы определить. Если есть лучший сайт StackExchange для этого вопроса, пожалуйста, помогите мне переместить его туда.

ОБНОВЛЕНИЕ № 1 - Апрель 2013

Мы выбрали блоки в качестве средства определения обратных вызовов для наших обработчиков событий. Мы в основном довольны этим выбором и не планируем удалять поддержку слушателей на основе блоков. У него было два заметных недостатка: управление памятью и импеданс дизайна.

Управление памятью

Блоки легче всего использовать в стеке. Создание долгоживущих блоков путем копирования их в кучу вносит интересные проблемы управления памятью.

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


Особенность библиотеки в том, что вы можете лишь в некоторой степени предвидеть, как она будет использоваться. поэтому вам нужно предоставить максимально простое и открытое решение, знакомое пользователям.

  • Для меня все это лучше всего подходит для делегации. Хотя вы правы, это может иметь место только на слушателе (делегате), это означает отсутствие ограничений, поскольку пользователь может написать класс в качестве делегата, который знает обо всех желаемых слушателях и информирует их. Конечно, вы можете предоставить регистрационный класс. это вызовет методы делегата для всех зарегистрированных объектов.
  • Блоки так же хороши.
  • то, что вы называете селекторами, называется target / action и простым, но мощным.
  • KVO, по-моему, также не является оптимальным решением для меня, так как это может ослабить инкапсуляцию или привести к неправильной модели использования классов вашей библиотеки.
  • NSNotifications приятно информировать об определенных событиях, но пользователи не должны быть вынуждены использовать их, поскольку они довольно неформальные. и ваши классы не смогут узнать, есть ли кто-то настроенный.

некоторые полезные мысли по API-дизайну: http://mattgemmell.com/2012/05/24/api-design/


Я думаю, что правильно сделать, реализовать оба, использовать его в качестве клиента, и посмотреть, что чувствует себя наиболее естественно. У обоих подходов есть свои преимущества, и они действительно зависят от контекста и того, как вы ожидаете использовать SDK.

Основным преимуществом селекторов является простое управление памятью - при условии, что клиент правильно регистрируется и отменяется, ему не нужно беспокоиться об утечках памяти. С блоками управление памятью может быть сложным, в зависимости от того, что клиент делает внутри блока. Также проще выполнить модульное тестирование метода обратного вызова. Блоки, конечно, могут быть написаны для тестирования , но из того, что я видел, это не обычная практика.

Основным преимуществом блоков является гибкость - клиент может легко ссылаться на локальные переменные, не делая их иварами.

Поэтому я думаю, что это зависит только от варианта использования - нет «объективного правильного ответа» на такой общий вопрос дизайна.





objective-c-blocks