HarmonyOS开发者限时福利来啦!最高10w+现金激励等你拿~ 了解详情
写点什么

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

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

关注

评论

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

视野 | OpenSearch,云厂商的新选择?

RadonDB

数据库 搜索引擎; Elastic Search

原因揭秘!为什么选择 Pulsar 而非 Kafka

Apache Pulsar

kafka 开源 架构 分布式 Apache Pulsar

单机训练6000万类视觉分类模型,飞桨大规模分类库PLSC做到了

百度开发者中心

飞桨 视觉分类 plsc

巅峰对话在线研讨 Q&A:Oracle Database 21c vs openGauss 2.0新特性解读和架构演进

墨天轮

数据库 oracle opengauss

拒绝编译等待 - 动态研发模式 ARK

字节跳动终端技术

ios 字节跳动 移动开发

许式伟:Go+ Together丨Go+ 1.0 发布会干货分享

七牛云

Go 语言

参会指南 | 2021MongoDB南京技术沙龙

MongoDB中文社区

mongodb

林昊:开发者如何提升写代码的硬实力丨Go+ 1.0 发布会干货分享

七牛云

Go 语言

干货分享:细说双 11 直播背后的压测保障技术

阿里巴巴中间件

阿里云 云原生 中间件 全链路 PTS

HarmonyOS 3.0.0开发者预览版全新发布

HarmonyOS开发者

HarmonyOS ArKUI 3.0 ArkCompiler 3.0

【堡垒机小知识】堡垒机是硬件还是软件?

行云管家

网络安全 信息安全 数据安全 堡垒机

多变的智能降噪

睿象云

运维 告警 智能运维 告警管理

阿里IM技术分享(六):闲鱼亿级IM消息系统的离线推送到达率优化

JackJiang

架构设计 即时通讯 IM

【高并发】通过源码深度分析线程池中Worker线程的执行流程

冰河

Java 并发编程 多线程 高并发 异步编程

vue3 学习笔记 (一)——mixin 混入

码仔

Vue3 mixin

明道云商业化成果巡礼|2021年11月

明道云

频繁更新主机,导致pod ip不够解决方法

ilinux

北省廊坊市本地有等保测评机构吗?在哪里?

行云管家

网络安全 等保 等级保护 等保测评 廊坊

无处不在的 Kubernetes,难用的问题解决了吗?

阿里巴巴中间件

阿里云 Kubernetes 容器 云原生 中间件

程序员的硬核浪漫 — 女友专属语聊房(内附源码)

ZEGO即构

音视频 语聊房 demo源码 即构科技

许式伟:Go+ v1.x 的设计与实现丨Go+ 公开课 • 第一期

七牛云

Go 语言 goplus

【体验有礼】Serverless 极速搭建 Hexo 博客

阿里巴巴中间件

阿里云 Serverless 云原生 Hexo 中间件

🏆【Alibaba中间件技术系列】「RocketMQ技术专题」小白专区之领略一下RocketMQ基础之最!

洛神灬殇

RocketMQ 消息队列 Alibaba技术 11月日更

构建 Snowpack + React + Typescript + Electron的Desktop App

吴脑的键客

typescript Electron React webpack

Go+ Together!Go+ 1.0 发布会暨 Go+ 开发者基金会启动仪式圆满结束!

七牛云

Go 语言

iOS开发面试和底层学习视频整理合集

iOSer

ios iOS面试 ios开发 iOS视频学习 iOS涨薪

网易云信发布虚拟形象实时互动融合 SDK ,元宇宙大幕即将开启

网易云信

人工智能 数字化 元宇宙

恒源云(GPUSHARE)_基于梯度的NLP对抗攻击方法

恒源云

人工智能 深度学习

Microsoft SQL Server 迁移利器,Babelfish for Aurora PostgreSQL 上线!

亚马逊云科技 (Amazon Web Services)

数据库 开源 源代码

黄东旭:写给后端程序员看的认知心理学丨Go+ 1.0 发布会干货分享

七牛云

Go 语言

为AI另辟蹊径的“小”数据

澳鹏Appen

人工智能 大数据 小数据 数据标注 训练数据

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