ReactiveCocoa(其简称为 RAC)是由 Github 工程师们开发的一个应用于 iOS 和 OS X 开发的函数响应式编程新框架。ReactiveCocoa 为开发者带来了函数式编程和响应式编程的思想,被 Mattt Thompson 称为开启一个新 Objective-C 纪元。InfoQ 此次专访了花瓣网移动开发主管李忠,另外作为 ArchSummit 深圳 2014 大会《移动互联网,一浪高过一浪》专题的讲师,李忠将会分享 ReactiveCocoa 在花瓣客户端的实践。
InfoQ:使用 ReactiveCocoa 与直接使用 Cocoa 框架相比,性能上(事件的响应速度、回调速度)是否会有影响?
李忠:ReactiveCocoa 底层的实现是比较复杂的,在性能上确实会有一定的影响。一个简单的 [signal subscribeNext: ^(id x){}] 就会有造成很深的 callback stack(近 40 次的调用),相比纯 KVO 不到 10 次的调用,速度上慢了至少 1 个数量级。不过尽管如此,只要 subscribe 的次数不要过多,性能上还是可以接受的。
在事件响应上,RAC 比 KVO 慢了大概 5 倍,不过问题不大,在 iPhone5 上测了下,也就 1ms 多一点,绝大多数的使用场景都不会有问题。
在开发 Mac App 时,可以使用 Cocoa Bindings,但 iOS 却不支持,可能也是出于性能上的考虑。既然 RAC 的性能不如直接使用原生的高,还有必要用它么?我觉得还是有的,性能是我们选择框架的一个参考因素,但不是决定性的因素。开发者在足够了解 RAC 的情况下,RAC 可以提高开发效率并帮助开发者编写更易维护的代码,这两点就值得我们去研究、使用它。
InfoQ:使用 ReactiveCocoa 等于是放弃了 xib 或 StoryBoard,这样的话在开发界面的时候就需要通过代码去控制,这是否会降低开发效率?
李忠:iOS 开发 UI 界面主要有 3 种方式,手写 UI 代码、使用 xibs 来组织 UI、使用 StoryBoard 来组织 xibs,3 种方式各有优缺点。
- 手写 UI 代码 (也是我目前采用的方式) 既然 xib 可以做的事情,代码都可以做到,而且 xib 做不了的事情,代码也可以做到,那为什么不直接用代码来写呢。很多人担心开发效率上会是一个很大的问题,我觉得或许会慢一点,但问题不大,尤其是结合了这样的 UIView Helper 之后。还有就是涉及到多人开发时,可以减少冲突,尤其是每个人负责各自的模块,基本不会出现这个情况。
- 使用 xibs 来组织 UI 这也是不少开发者采用的模式,跟手写 UI 相比,最大的好处是直观且高效。Xcode4 的 xib 文件结构复杂且臃肿,很容易产生冲突,不过好在 Xcode5 对它进行了很大的改进,结构更加简单且易读。不过由于 UI 既可以在 xib 里调整,也可以在代码里调整,甚至是代码的不同地方进行调整,调试和维护都容易出现问题。
- 使用 StoryBoard 好处很明显:非常直观。一共有多少个页面,每个页面是做什么的,页面之间如何关联都可以看得很清楚。问题也很大:多人协作,很容易出现冲突,要频繁地解决冲突还是挺影响效率的。当然如果只是一个人开发,那就没有问题了。
所以三种方式各有优劣,而使用 RAC 并不会强制你使用代码去构建 UI,依然可以用 xib/StoryBoard,它改变的是编程模式,对 UI 的影响其实不大。另外 RAC 还提供了一套 UIKit Extension,很多需要 Delegate/Target-Action 的 UI,可以直接使用 RAC 的方法,这也带来了很大的便利。
InfoQ:苹果每年都会有新的工具、新的 API 开放出来,比如 iOS 6 之后可以实现界面元素的相对布局等等,ReactiveCocoa 可以支持新的功能么?或者是能否基于 ReactiveCocoa 进行自定义扩展?
李忠:好比有一座房子,房子的主人每年都会对里面的家具做一些调整,如灯泡从白炽灯变成节能灯,洗衣机从半自动变成了全自动等等,也会新添置一些器材,如为了更爽地看世界杯,买了个投影仪,或为了更方便地打扫房间,买了个 iRobot 等等。
这座房子就好像 Cocoa,对家具的调整就好比新的 API,新的工具好比新的器材。而 RAC 并不会对现有的家具造成影响,它改变的只是墙体的结构,让它更稳固。
以 AutoLayout 为例,它可以实现界面的相对布局,比如 [NSLayoutConstraint constraintsWithVisualFormat:options:metrics:views:],RAC 并不会干扰这一过程。 但 AutoLayout 的一个特点:描述 View 之间的关系,而不是动态的去计算,是挺符合 RAC 的理念的,所以 RAC 也可以用来做这件事情。比如有两个 View:parentView 和 childView,假如当 parentView 的 bounds 改变时,childView 也要跟着改变,就可以这么做:
[RACObserve(parentView, layer.bounds) subscribeNext:^(id bounds){ childView.frame = CGRectInset(bounds, 5, 5); }];
RAC 的开发者觉得这样可行,于是就有了 ReactiveCocoaLayout,所以是否有必要基于 RAC 进行自定义扩展,需要看是否符合 RAC 的理念。
InfoQ:使用 ReactiveCocoa 带给你和你的团队最大的好处是什么? 最大的弊端是什么?
李忠:先来说说弊端吧,RAC 最大的问题在于它跟正常的编程模式太不一样了,就像第一次穿上溜冰鞋,很多人都会觉得不习惯,然后各种摔跤,因为不能再用“走路”的模式去实践了。所以学习成本是个很大的挑战。
其次 RAC 没有被大规模采纳,很少有人分享 Best Practices,或相关的文章,这时“贸然”地用到项目里,如果影响了开发效率怎么办?项目不能如期交付怎么办?其他团队成员不够熟悉怎么办?遇到问题找不到解决方案怎么办?这些都是要考虑的因素,所以如果要使用,必须对它有相当程度的了解,因此潜在的风险也是个大问题。
有一天同事 Dismory 说他已经用 RAC 开发了一个 App,并且感觉很不错,于是就决定在开发花瓣时用一下。因为我们是模块化开发,每个人会分到多个模块,所以也并不要求每个人都使用 RAC,可以按照自己最熟悉的方式去写,这也进一步降低了风险。
以前没有用 RAC 写过一个完整的项目,自然会遇到不少问题。最大的问题是:如何用 RAC 的理念去思考?因为不够熟悉,所以代码往往两不像,既不像 RAC,也不像 Cocoa。于是我就开始翻 issues 列表,看 RAC 作者写的 App 以及各类文章,慢慢地有点 get the point 了,写起来也顺手了。也会跟团队成员分享经验,讨论遇到的问题。开发效率的提升,代码复杂度降低这两点就是最大的好处。
InfoQ:今年的 WWDC 大会上苹果发布了 Swift 语言,未来苹果应该会大力支持 swift, 你认为 ReactiveCocoa 是否会基于 Swift 开发新版本?难度大吗?
李忠:由于 ObjectiveC 语言自身的限制,也影响到了 RAC 的一些特性,比如无法根据一个 Signal 得知它的 sendNext value 的类型,这是很不方便的,要么推断它的类型,要么去看接口说明,如果没有说明,那只能看源码。而 Swift 的 Generic 特性正好可以弥补这点。
除此之外,因为 Swift 没有 KVO,而 RAC 又是基于 KVO 实现的,所以如果要用 Swift 来重写,底层的改动还是挺大的。不过看起来他们正打算这么做。
这就会带来一些问题,如果项目是用 ObjectiveC 写的,那么就无法调用 Swift 的 Generic 方法,或者其他 Swift 具备的特性。另外目前 Swift 语言还没有到稳定版,接口和使用上也存在变动的可能。
我觉得他们应该是认同 Swift,且相信它会在将来成为主力开发语言,所以不如一次性地支持到位。如果还是使用 OC,那么可以用 RAC2,如果使用 Swift,那么就可以用 RAC3。
RAC3 借鉴了.NET 的 Rx 思想,通过 Observer / Observable / Enumerator / Enumerable 这 4 个基础类来实现 push/pull driven streams,架构上也更清晰了,使用 Swift 来实现这些特性应该也没什么问题。至于难度么,Just trust the github guys。
InfoQ:使用 ReactiveCocoa 需要时间成本,你认为值吗?是否建议新手直接使用 ReactiveCocoa 开发程序?
李忠:相比于其他的框架,ReactiveCocoa 的学习曲线更加陡峭,也就意味着需要花更多的时间。如果对 Cocoa 的设计模式、理念和常用 Framework 都已经很熟悉,也做过了几个成熟的 App,那么可以去更深入地了解下,比如如何用 RAC 的方式去解决 Cococa 编程遇到的问题,如何写出更 RAC 的代码等等。
有两种方法可以写出 bug-free 的代码。 1) 使用那些让 bug 更少的技术 2) 用自己熟悉的技术。如果对第一点吃不准,那么只使用第二点也没什么问题。
不建议新手直接使用 RAC 去开发程序,如果能做到这点,已经不是新手了,至少有不错的编程基础。如果只是自己做 Side Project 还行,涉及到多人合作,说服别人使用也是个难题,毕竟 RAC 不够 popular,且有着不可控的风险,而且将来别人来维护代码也会是个问题。
Cocoa 编程还有很多的挑战,这些不是学会了 RAC 就能解决的,对于大多数人我还是建议先看看,不用急着就在项目里使用,等 RAC3.0 出来后再考虑也不迟。
InfoQ:ReactiveCocoa 为 iOS 开发者带来了函数响应式编程, 统一了消息传递机制,你认为 ReactiveCocoa 还有哪些需要完善的地方?
李忠:ReactiveCocoa 需要完善的地方包括:
- 性能 跟 pure KVO 相比,还是差了不少。比如光是 subscribNext 就慢了 1 个数量级,接收到新的 value 也慢了 5 倍左右。如果 signal 一多,subscription 也会跟着多起来,性能问题就会慢慢浮出水面。
- signal 的 sendNext value 类型未知 感觉回到了脚本语言。只能根据 signal 名字去推断,或是看源码,不够 cool,且影响效率,还有出错的可能(比如正常应该发送 Number 的,忽然发送了 String),不过使用 Swift 应该能够解决这个问题。
- 调试 目前的 callback stack 实在是深了点,最简单的 [signal subscribeNext^(id x){}] 就会有近 40 次的调用,项目跑起来,如果挂在了某个地方,往前追溯就得绕过那「厚厚」的一层 RAC 调用,真心累啊。
- 命名 作为编程界的两大难题之一,RAC 在这块也有改进的空间,比如他们内部就会讨论 subscribe 这个名字是不是有问题,再想想 RACChannel 的命名等等,如果名字能够让使用者一看就明白,也算是降低了学习成本。
评论