写点什么

ReactiveCocoa - iOS 开发的新框架

  • 2014-02-11
  • 本文字数:3322 字

    阅读完需:约 11 分钟

ReactiveCocoa (其简称为 RAC)是由 Github 开源的一个应用于 iOS 和 OS X 开发的新框架。RAC 具有函数式编程和响应式编程的特性。它主要吸取了.Net 的 Reactive Extensions 的设计和实现。本文将详细介绍该框架试图解决什么问题,以及其用法与特点。

ReactiveCocoa 试图解决什么问题

经过一段时间的研究,我认为 ReactiveCocoa 试图解决以下 3 个问题:

  1. 传统 iOS 开发过程中,状态以及状态之间依赖过多的问题
  2. 传统 MVC 架构的问题:Controller 比较复杂,可测试性差
  3. 提供统一的消息传递机制

传统 iOS 开发过程中,状态以及状态之间依赖过多的问题

我们在开发 iOS 应用时,一个界面元素的状态很可能受多个其它界面元素或后台状态的影响。

例如,在用户帐户的登录界面,通常会有 2 个输入框(分别输入帐号和密码)和一个登录按钮。如果我们要加入一个限制条件:当用户输入完帐号和密码,并且登录的网络请求还未发出时,确定按钮才可以点击。通常情况下,我们需要监听这两个输入框的状态变化以及登录的网络请求状态,然后修改另一个控件的enabled状态。

传统的写法如下(该示例代码修改自 ReactiveCocoa 官网 ) :

复制代码
static void *ObservationContext = &ObservationContext;
(void)viewDidLoad {
[super viewDidLoad];
[LoginManager.sharedManager addObserver:self
forKeyPath:@"loggingIn"
options:NSKeyValueObservingOptionInitial
context:&ObservationContext];
[self.usernameTextField addTarget:self action:@selector(updateLogInButton)
forControlEvents:UIControlEventEditingChanged];
[self.passwordTextField addTarget:self action:@selector(updateLogInButton)
forControlEvents:UIControlEventEditingChanged];
}
- (void)updateLogInButton {
BOOL textFieldsNonEmpty = self.usernameTextField.text.length > 0
&& self.passwordTextField.text.length > 0;
BOOL readyToLogIn = !LoginManager.sharedManager.isLoggingIn && !self.loggedIn;
self.logInButton.enabled = textFieldsNonEmpty && readyToLogIn;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (context == ObservationContext) {
[self updateLogInButton];
} else {
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}

RAC 通过引入信号(Signal)的概念,来代替传统 iOS 开发中对于控件状态变化检查的代理(delegate)模式或 target-action 模式。因为 RAC 的信号是可以组合(combine)的,所以可以轻松地构造出另一个新的信号出来,然后将按钮的enabled状态与新的信号绑定。如下所示:

复制代码
RAC(self.logInButton, enabled) = [RACSignal
combineLatest:@[
self.usernameTextField.rac_textSignal,
self.passwordTextField.rac_textSignal,
RACObserve(LoginManager.sharedManager, loggingIn),
RACObserve(self, loggedIn)
] reduce:^(NSString *username, NSString *password, NSNumber *
loggingIn, NSNumber *loggedIn) {
return @(username.length > 0 && password.length > 0 && !
loggingIn.boolValue && !loggedIn.boolValue);
}];

可以看到,在引入 RAC 之后,以前散落在action-target或 KVO 的回调函数中的判断逻辑被统一到了一起,从而使得登录按钮的enabled状态被更加清晰地表达了出来。

除了组合(combine)之外,RAC 的信号还支持链式(chaining)和过滤(filter),以方便将信号进行进一步处理。

试图解决 MVC 框架的问题

对于传统的 Model-View-Controller 的框架,Controller 很容易变得比较庞大和复杂。由于 Controller 承担了 Model 和 View 之间的桥梁作用,所以 Controller 常常与对应的 View 和 Model 的耦合度非常高,这同时也造成对其做单元测试非常不容易,对 iOS 工程的单元测试大多都只在一些工具类或与界面无关的逻辑类中进行。

RAC 的信号机制很容易将某一个 Model 变量的变化与界面关联,所以非常容易应用 Model-View-ViewModel 框架。通过引入 ViewModel 层,然后用 RAC 将 ViewModel 与 View 关联,View 层的变化可以直接响应 ViewModel 层的变化,这使得 Controller 变得更加简单,由于 View 不再与 Model 绑定,也增加了 View 的可重用性。

因为引入了 ViewModel 层,所以单元测试可以在 ViewModel 层进行,iOS 工程的可测试性也大大增强了。InfoQ 也曾撰文介绍过 MVVM:《MVVM 启示录》

统一消息传递机制

iOS 开发中有着各种消息传递机制,包括 KVO、Notification、delegation、block 以及 target-action 方式。各种消息传递机制使得开发者在做具体选择时感到困惑,例如在 objc.io 上就有专门撰文破船的翻译 ),介绍各种消息传递机制之间的差异性。

RAC 将传统的 UI 控件事件进行了封装,使得以上各种消息传递机制都可以用 RAC 来完成。示例代码如下:

复制代码
// KVO
[RACObserve(self, username) subscribeNext:^(id x) {
NSLog(@" 成员变量 username 被修改成了:%@", x);
}];
// target-action
self.button.rac_command = [[RACCommand alloc] initWithSignalBlock:
^RACSignal *(id input) {
NSLog(@" 按钮被点击 ");
return [RACSignal empty];
}];
// Notification
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(keyboardDidChangeFrameNotificationHandler:)
name:UIKeyboardDidChangeFrameNotification object:nil];

RAC 的RACSignal 类也提供了createSignal方法来让用户创建自定义的信号,如下代码创建了一个下载指定网站内容的信号。

复制代码
(RACSignal *)urlResults {
return [RACSignal createSignal:^RACDisposable *(id<racsubscriber> subscriber) { NSError *error; NSString *result = [NSString stringWithContentsOfURL: [NSURL URLWithString:@"http://www.devtang.com"] encoding:NSUTF8StringEncoding error:&error]; NSLog(@"download"); if (!result) { [subscriber sendError:error]; } else { [subscriber sendNext:result]; [subscriber sendCompleted]; } return [RACDisposable disposableWithBlock:^{ NSLog(@"clean up"); }]; }]; }</racsubscriber>

如何使用 ReactiveCocoa

ReactiveCocoa 可以在 iOS 和 OS X 的应用开发中使用,对于 iOS 开发者,可以将 RAC 源码下载编译后,使用编译好的libReactiveCocoa-iOS.a文件。

开发者也可以用 CocoaPods 来设置目标工程对 ReactiveCocoa 的依赖,只需要编辑 Podfile 文件,增加如下内容即可:

复制代码
pod 'ReactiveCocoa', ‘2.0'

ReactiveCocoa 的特点

RAC 在应用中大量使用了 block,由于 Objective-C 语言的内存管理是基于引用计数 的,为了避免循环引用问题,在block 中如果要引用self,需要使用 @weakify(self)@strongify(self)来避免强引用。另外,在使用时应该注意 block 的嵌套层数,不恰当的滥用多层嵌套 block 可能给程序的可维护性带来灾难。

RAC 的编程方式和传统的 MVC 方式差异巨大,所以需要较长的学习时间。并且,业界内对于 RAC 并没有广泛应用,这造成可供参考的项目和教程比较欠缺。另外,RAC 项目本身也还在快速演进当中,1.x 版本和 2.x 版本 API 改动了许多,3.0 版本也正在快速开发中,对它的使用也需要考虑后期的升级维护问题。

作为一个 iOS 开发领域的新开源框架,ReactiveCocoa 带来了函数式编程和响应式编程的思想,值得大家关注并且学习。

作者简介:

唐巧,资深 iOS 开发者和 Blogger,曾开发有道云笔记、猿题库和粉笔网的 iOS 客户端。他维护着 iOS 开发博客 http://www.devtang.com/ 和微信 iOS 开发公众账号 iosDevTips。唐巧在 2014 年 QCon 北京将进行一场 iOS 开发进阶的培训,介绍如何基于 CoreText 自己实现一个排版引擎

2014-02-11 00:2540480
用户头像

发布了 65 篇内容, 共 57.2 次阅读, 收获喜欢 23 次。

关注

评论

发布
暂无评论
发现更多内容

英特尔推动集成光电的发展,用于数据中心

E科讯

ONES 收购 Tower,五源资本合伙人对话两位创始人

万事ONES

项目管理 团队协作 ONES Tower 收购

一个真正0基础小白学习前端开发的心路历程

华为云开发者联盟

开发 开发小白 0基础

性能压测

jorden wang

谁能阻止世纪互联星光?2020年第11期北京机房网络质量评测数据排行榜发布

博睿数据

英特尔神经拟态生态系统发展和研究的最新进展

E科讯

【技术分享】浅谈RTC及Agora RTC SDK集成介绍

Hanson

《前端实战总结》之使用CSS3实现酷炫的3D旋转透视

徐小夕

css3 大前端 CSS小技巧

跨架构编程不再难,英特尔机器编程工具迎来重磅更新

E科讯

《技术男征服美女HR》—Fiber、Coroutine和多线程那些事

太白上仙

Java 程序员 面试 后端 多线程

ONES 收购知名协作工具 Tower

万事ONES

团队协作 高效 研发管理工具 收购 资讯

架构设计之路-1

5加3彩票

架构师 代码规范

架构师训练营第 11 周课后练习

叶纪想

极客大学架构师训练营

IntelliJ IDEA 2020.3正式发布,年度最后一个版本很讲武德

YourBatman

IDEA 新特性 2020.3

即使不会node.js,拖拽就可完成数据的可视化展示

华为云开发者联盟

node.js 数据 可视化

1分钟解密:博睿大数据核心引擎Bonree Zeus六大优势

博睿数据

想了解任务型对话机器人,我们先从自然语言理解聊起

华为云开发者联盟

人工智能 机器人 自然语言

挑战赛 | 话题王者VS互动先锋(第一季)

InfoQ写作社区官方

话题讨论 热门活动

架构师训练营第 1 期 -week11

习习

国产电子表格Luckysheet后台也开源了!支持在线协作,一键docker私有部署

奇异石榴果

Java Excel SpreadJS 表格控件

腾讯大牛整合Java+spring5系统学习架构,神乎其技

小Q

Java 学习 编程 面试 spring 5

面试无忧:源码+实践,讲到MySQL调优的底层算法实现

996小迁

Java 架构 面试

给你一个亿的keys,Redis如何统计?

不才陈某

redis

新闻|Babelfish使PostgreSQL直接兼容SQL Server应用程序

PostgreSQLChina

数据库 postgresql 开源

微软最强 Python 自动化工具开源了!不用写一行代码!

星安果

Python 微软 自动化 自动化测试 playwright

加码线下,新荣耀“破题”场景经济

脑极体

javascript开发后端程序的神器nodejs

程序那些事

Java 后端 nodejs koa 程序那些事

关于binlog,这个参数能不能用?

Simon

MySQL Binlog

智慧公安扫码一键定位报警系统搭建

t13823115967

智慧公安 智慧公安扫码 一键定位报警系统搭建

英特尔发布第二代Horse Ridge低温量子控制芯片

E科讯

Linux平台中调试C/C++内存泄漏方法 (腾讯和MTK面试的时候问到的)

linux大本营

c++ Linux 后台开发 架构师

ReactiveCocoa - iOS开发的新框架_QCon_唐巧_InfoQ精选文章