Ответ 1
Управление памятью ReactiveCocoa довольно честно, но достойным конечным результатом является то, что вам не нужно сохранять сигналы для их обработки.
Если структура потребовала, чтобы вы сохранили каждый сигнал, было бы гораздо более громоздким в использовании, особенно для одноразовых сигналов, которые используются как фьючерсы (например, сетевые запросы). Вам нужно будет сохранить долгоживущий сигнал в свойстве, а затем также обязательно очистите его, когда закончите с ним. Не весело.
Подписчики
Прежде чем идти дальше, я должен указать, что subscribeNext:error:completed:
(и все его варианты) создают неявный подписчик, используя данные блоки. Любые объекты, на которые ссылаются эти блоки, будут сохраняться как часть подписки. Как и любой другой объект, self
не будет сохранен без прямой или косвенной ссылки на него.
(Основываясь на формулировке вашего вопроса, я думаю, вы уже это знали, но это может быть полезно для других.)
Конечные или короткоживущие сигналы
Наиболее важным ориентиром для управления памятью RAC является то, что подписка автоматически заканчивается после завершения или ошибки, а абонент удаляет. Чтобы использовать круглый ссылочный пример:
calling scope -> disposable -> signal -> subscriber -> calling scope
... это означает, что отношение signal -> subscriber
срывается, как только заканчивается signal
, прерывая цикл сохранения.
Это часто все, что вам нужно, потому что время жизни RACSignal
в памяти, естественно, будет соответствовать логическому времени жизни потока событий.
Бесконечные сигналы
Бесконечные сигналы (или сигналы, которые живут так долго, что они могут быть бесконечными), однако, никогда не будут разрушаться естественным образом. Это то, где располагают одноразовые светильники.
Устранение подписки приведет к удалению связанного абонента и просто вообще очистит все ресурсы, связанные с этой подпиской. Для этого одного абонента это точно так же, как если бы сигнал был завершен или ошибочен, за исключением того, что на сигнал не отправлено окончательное событие. Все остальные абоненты останутся нетронутыми.
Однако, как правило, , если вам нужно вручную управлять жизненным циклом подписки, возможно, лучший способ сделать то, что вы хотите.. Методы типа -take:
или -takeUntil:
будут обрабатывать удаление для вас, и вы получите более высокую абстракцию.
Сигналы, полученные из self
Здесь все еще немного сложного среднего дела. В любое время, когда время жизни сигнала привязано к области вызова, вам будет сложнее перерыв.
Это обычно происходит при использовании RACAble()
или RACAbleWithStart()
по ключевому пути, относящемуся к self
, а затем применяя блок, который должен захватывать self
.
Самый простой ответ - просто зафиксировать self
слабо:
__weak id weakSelf = self;
[RACAble(self.username) subscribeNext:^(NSString *username) {
id strongSelf = weakSelf;
[strongSelf validateUsername];
}];
Или после импорта включенного EXTScope.h заголовка:
@weakify(self);
[RACAble(self.username) subscribeNext:^(NSString *username) {
@strongify(self);
[self validateUsername];
}];
(Замените __weak
или @weakify
на __unsafe_unretained
или @unsafeify
, соответственно, если объект не поддерживает слабые ссылки.)
Тем не менее, , вероятно, лучший шаблон, который вы могли бы использовать вместо этого. Например, приведенный выше пример может быть написан как:
[self rac_liftSelector:@selector(validateUsername:)
withObjects:RACAble(self.username)];
или
RACSignal *validated = [RACAble(self.username) map:^(NSString *username) {
// Put validation logic here.
return @YES;
}];
Как и в случае с бесконечными сигналами, обычно есть способы избежать ссылки self
(или любого объекта) из блоков в цепочке сигналов.
Вышеупомянутая информация - это все, что вам нужно, чтобы эффективно использовать ReactiveCocoa. Тем не менее, я хочу рассмотреть еще один момент, только для технически любопытных или для всех, кто заинтересован в участии в RAC:
Я также замечаю, что реализация сохраняет список глобальных активных сигналов процесса.
Это абсолютно верно.
Цель проекта "не сохранять необходимое" задает вопрос: откуда мы узнаем, когда сигнал должен быть освобожден? Что, если он был только что создан, избежал пула автозапуска и еще не был сохранен?
Реальный ответ: мы этого не делаем, но мы обычно можем предположить, что вызывающий абонент сохранит сигнал в текущей итерации цикла запуска, если они захотят его сохранить.
Следовательно:
- Созданный сигнал автоматически добавляется к глобальному набору активных сигналов.
- Сигнал будет ждать одного прохода основного цикла запуска, а затем удаляется из активного набора, если у него нет подписчиков. Если бы сигнал не был сохранен каким-либо образом, он бы снял с этого момента.
- Если что-то подписалось в этой итерации цикла запуска, сигнал остается в наборе.
- Позже, когда все подписчики ушли, снова запускается # 2.
Это может иметь неприятные последствия, если цикл цикла рекурсивно (например, в модальном цикле событий в OS X), но это делает жизнь сетевого пользователя намного проще для большинства или всех других случаев.