时隔16年Jeff Barr重返10.23-25 QCon上海站,带你看透AI如何重塑软件开发! 了解详情
写点什么

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:2540734
用户头像

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

关注

评论

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

多指标异常检测方法综述

云智慧AIOps社区

AIOPS 异常检测 技术学习 智能运维 指标

拒绝裸奔,为 Elasticsearch 设置账号密码(qbit)

qbit

https 安全 Kibana

第7章-《Linux一学就会》-Centos8 用户管理

学神来啦

Linux linux运维 linux学习 Linux教程

阿里云天池赛题解析——深度学习篇重磅发布!

博文视点Broadview

被客户像小学生一样训话

boshi

创业

netty系列之:分离websocket处理器

程序那些事

Java 架构 Netty 程序那些事

乌镇回溯 | 构建网络空间命运共同体,旺链科技做了哪些?

旺链科技

数字经济 产业区块链 世界互联网大会

CRM是什么,你有认真了解过CRM吗?

低代码小观

企业管理 CRM

在线黑客帝国文字效果生成工具

入门小站

工具

基于云的 CRM 能为您的业务提供哪些服务?

低代码小观

企业管理 CRM

13个VSCode使用技巧,开启高效的开发模式

华为云开发者联盟

vscode 日志 开发 插件 Git存储库

万亿养老市场如何抢占商机?云巢智慧康养物联网加速器,三招化解ISV痛点!

华为云开发者联盟

物联网 华为云 iotda 沃土云创计划 智慧康养

融云 IM+RTC+X「全」通信解决方案,赋能全平台、多语言开发者

融云 RongCloud

通信 技术栈 语言 & 开发

Nacos-Group

平凡人生

基于虹软SDK,适配Camera1、Camera2、CameraX,实现人脸识别(Android)

小驰笔记

android 音视频 人脸识别 引航计划

[外文资源]最好的 Golang 博客

baiyutang

golang 9月日更

月度发布 | 极狐GitLab14.3升级40+新功能!

极狐GitLab

带你了解数仓安全测试的TLS协议

华为云开发者联盟

安全 通信 密钥 SSL/TLS协议 加密通信

StartDT Hackathon | 泛元数据:让数据成为资产

奇点云

Go 语言实现 WebSocket 推送

全象云低代码

websocket 低代码开发 Go 语言

linux之登录式shell和非登录式shell

入门小站

Linux

Prometheus 2.30.0 新特性

耳东@Erdong

release Prometheus 9月日更

详解html5新增的标签与css3中伪类和伪元素

你好bk

html5 css3 大前端 基础技能

如何利用国产图数据库打造金融行业方案?

星环科技

CRM系统如何帮助你的业务?

低代码小观

企业管理 CRM

Python代码阅读(第30篇):找到列表中的奇偶异常项

Felix

Python 编程 Code Programing 阅读代码

扒一扒面向对象编程的另一面

华为云开发者联盟

编程 面向对象 对象 对象编程

Talk to AI,揭秘背后的语音识别数据

澳鹏Appen

语音 nlp 语音识别 训练数据 语音识别模型

译文 | MySQL 8.0 密码管理策略(一)

RadonDB

MySQL 数据库

分布式系统都要遵守的CAP

卢卡多多

CAP 9月日更

美女程序“媛”:从工程师到架构师,我的代码人生

融云 RongCloud

程序员 代码人生

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