RACSignal
Фреймворк основан на сигналах. Сигнал — это объект, в котором хранится ссылка на наблюдаемый объект и ссылки на подписчиков. Когда сигнал видит, что наблюдаемый объект изменился, он пересылает новое значение подписчикам. Подписчиком может быть другой сигнал, блок, а также любой объект.
Пример 1. Сигнал, наблюдающий свойство name и передающий значения в блок:
User *user = [User new];
RACSignal *userNameSignal = RACObserve(user, name);
[userNameSignal subscribeNext:^(NSString *newValue) {
NSLog(@"New value: %@", newValue);
}];
user.name = @"ivan";
//Вывод в консоли:
//New value: ivan
Преобразование сигналов
До того, как новое значение объекта дойдет до подписчика, оно может быть подвергнуто различным преобразованиям. Такими как:
- изменение значения (map)
- фильтрация (filter, ignore)
- объединение с другим значением (combine)
- упрощение нескольких сигналов в одно (reduce)
Пример 2. Продолжая первый пример, изменим передаваемое значение:
User *user = [User new];
RACSignal *userNameSignal = RACObserve(user, name);
[[userNameSignal map:^id(NSString *newValue) {
return newValue.capitalizedString;
}] subscribeNext:^(NSString *newValue) {
NSLog(@"New value: %@", newValue);
}];
user.name = @"ivan";
//Теперь все значения будут передаваться подписчикам с большой буквы.
//Вывод в консоли:
//New value: Ivan
Биндинги
Преобразования не были бы так полезны, если бы не биндинги. Они позволяют создать зависимость одного свойства от другого:
- Кнопка авторизации активна только если введены логин и пароль
- Поля ввода логина и пароля отключены, когда происходит авторизация
- Свойство user.isValidName меняется автоматически когда меняется свойство user.name.
«Прибинженные» поля меняются сами, поэтому не надо в коде следить за актуальными значениями. Это полезно, например, в приложениях со сложной логикой интерфейса.
Продолжая предыдущий пример, сделаем биндинг для свойства user.login, чтобы подходящий логин автоматически предлагался пользователю на основе имени:
RAC(user, login) = [userNameSignal map:^id(NSString *name) {
return [name stringByReplacingOccurrencesOfString:@" " withString:@"_"].lowercaseString;
}];
user.name = @"Vladimir Petrov";
NSLog(@"Login: %@", user.login);
// Вывод в консоли:
// Login: vladimir_petrov
Минусы
Начав использовать ReactiveCocoa, уже трудно представить разработку без него. Но у этого фреймворка есть минусы, которые для кого-то могут быть существенными:
- Частое использование KVO может снизить производительность. Решается оптимизацией и неиспользованием RAC'a в высоконагруженных участках кода.
- Сложная отладка из-за частого использования блоков, особенно внутри самого ReactiveCocoa. Порой в поиске ошибки помогает лишь интуитивное понимание objective-c.
- Легко допустить ошибку и они приводят к неожиданным последствиям.
- Перегруженный синтаксис. Этой проблемы не будет с переходом на swift.
В заключение
Описанное выше — лишь малая часть того, что может ReactiveCocoa. Однако, даже применив только это, можно заметно упростить свою работу. Если эта статья будет кому-нибудь интересна, в следующей можно будет разобрать более сложные и интересные части этого фреймворка.
Автор: lostuser