花瓣網李忠:ReactiveCocoa是Cocoa的未來

ReactiveCocoa(其簡稱爲RAC)是由Github工程師們開發的一個應用於iOS和OS X開發的函數響應式編程新框架。ReactiveCocoa爲開發者帶來了函數式編程和響應式編程的思想,被Mattt Thompson稱爲開啓一個新Objective-C紀元。InfoQ此次專訪了花瓣網移動開發主管李忠。

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的命名等等,如果名字能夠讓使用者一看就明白,也算是降低了學習成本。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章