ReactiveCocoa(RAC)

什么是ReactiveCocoa

ReactiveCocoa(其簡稱為RAC)是由Github開源的一個應用于iOS和OS X開發(fā)的新框架。RAC具有函數(shù)式編程和響應式編程的特性。它主要吸取了.Net的Reactive Extensions的設(shè)計和實現(xiàn)。

ReactiveCocoa試圖解決什么問題

經(jīng)過一段時間的研究,我認為ReactiveCocoa試圖解決以下3個問題:

傳統(tǒng)iOS開發(fā)過程中,狀態(tài)以及狀態(tài)之間依賴過多的問題

傳統(tǒng)MVC架構(gòu)的問題:Controller比較復雜,可測試性差

提供統(tǒng)一的消息傳遞機制

傳統(tǒng)iOS開發(fā)過程中,狀態(tài)以及狀態(tài)之間依賴過多的問題

我們在開發(fā)iOS應用時,一個界面元素的狀態(tài)很可能受多個其它界面元素或后臺狀態(tài)的影響。

例如,在用戶帳戶的登錄界面,通常會有2個輸入框(分別輸入帳號和密碼)和一個登錄按鈕。如果我們要加入一個限制條件:當用戶輸入完帳號和密碼,并且登錄的網(wǎng)絡(luò)請求還未發(fā)出時,確定按鈕才可以點擊。通常情況下,我們需要監(jiān)聽這兩個輸入框的狀態(tài)變化以及登錄的網(wǎng)絡(luò)請求狀態(tài),然后修改另一個控件的enabled狀態(tài)。

傳統(tǒng)的寫法如下(該示例代碼修改自ReactiveCocoa官網(wǎng)) :

12345678910111213141516171819202122232425262728293031

staticvoid*ObservationContext=&ObservationContext;-(void)viewDidLoad{[superviewDidLoad];[LoginManager.sharedManageraddObserver:selfforKeyPath:@"loggingIn"options:NSKeyValueObservingOptionInitialcontext:&ObservationContext];[self.usernameTextFieldaddTarget:selfaction:@selector(updateLogInButton)forControlEvents:UIControlEventEditingChanged];[self.passwordTextFieldaddTarget:selfaction:@selector(updateLogInButton)forControlEvents:UIControlEventEditingChanged];}-(void)updateLogInButton{BOOLtextFieldsNonEmpty=self.usernameTextField.text.length>0&&self.passwordTextField.text.length>0;BOOLreadyToLogIn=!LoginManager.sharedManager.isLoggingIn&&!self.loggedIn;self.logInButton.enabled=textFieldsNonEmpty&&readyToLogIn;}-(void)observeValueForKeyPath:(NSString*)keyPathofObject:(id)objectchange:(NSDictionary*)changecontext:(void*)context{if(context==ObservationContext){[selfupdateLogInButton];}else{[superobserveValueForKeyPath:keyPathofObject:objectchange:changecontext:context];}}

RAC通過引入信號(Signal)的概念,來代替?zhèn)鹘y(tǒng)iOS開發(fā)中對于控件狀態(tài)變化檢查的代理(delegate)模式或target-action模式。因為RAC的信號是可以組合(combine)的,所以可以輕松地構(gòu)造出另一個新的信號出來,然后將按鈕的enabled狀態(tài)與新的信號綁定。如下所示:

123456789

RAC(self.logInButton,enabled)=[RACSignalcombineLatest:@[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的回調(diào)函數(shù)中的判斷邏輯被統(tǒng)一到了一起,從而使得登錄按鈕的enabled狀態(tài)被更加清晰地表達了出來。

除了組合(combine)之外,RAC的信號還支持鏈式(chaining)和過濾(filter),以方便將信號進行進一步處理。

試圖解決MVC框架的問題

對于傳統(tǒng)的Model-View-Controller的框架,Controller很容易變得比較龐大和復雜。由于Controller承擔了Model和View之間的橋梁作用,所以Controller常常與對應的View和Model的耦合度非常高,這同時也造成對其做單元測試非常不容易,對iOS工程的單元測試大多都只在一些工具類或與界面無關(guān)的邏輯類中進行。

RAC的信號機制很容易將某一個Model變量的變化與界面關(guān)聯(lián),所以非常容易應用Model-View-ViewModel框架。通過引入ViewModel層,然后用RAC將ViewModel與View關(guān)聯(lián),View層的變化可以直接響應ViewModel層的變化,這使得Controller變得更加簡單,由于View不再與Model綁定,也增加了View的可重用性。

因為引入了ViewModel層,所以單元測試可以在ViewModel層進行,iOS工程的可測試性也大大增強了。InfoQ也曾撰文介紹過MVVM:《MVVM啟示錄》

統(tǒng)一消息傳遞機制

iOS開發(fā)中有著各種消息傳遞機制,包括KVO、Notification、delegation、block以及target-action方式。各種消息傳遞機制使得開發(fā)者在做具體選擇時感到困惑,例如在objc.io上就有專門撰文破船的翻譯),介紹各種消息傳遞機制之間的差異性。

RAC將傳統(tǒng)的UI控件事件進行了封裝,使得以上各種消息傳遞機制都可以用RAC來完成。示例代碼如下:

123456789101112131415161718192021222324

// KVO[RACObserve(self,username)subscribeNext:^(idx){NSLog(@"成員變量 username 被修改成了:%@",x);}];// target-actionself.button.rac_command=[[RACCommandalloc]initWithSignalBlock:^RACSignal*(idinput){NSLog(@"按鈕被點擊");return[RACSignalempty];}];// Notification[[[NSNotificationCenterdefaultCenter]rac_addObserverForName:UIKeyboardDidChangeFrameNotificationobject:nil]subscribeNext:^(idx){NSLog(@"鍵盤Frame改變");}];// Delegate[[selfrac_signalForSelector:@selector(viewWillAppear:)]subscribeNext:^(idx){debugLog(@"viewWillAppear方法被調(diào)用 %@",x);}];

RAC的RACSignal類也提供了createSignal方法來讓用戶創(chuàng)建自定義的信號,如下代碼創(chuàng)建了一個下載指定網(wǎng)站內(nèi)容的信號。

12345678910111213141516171819

-(RACSignal*)urlResults{return[RACSignalcreateSignal:^RACDisposable*(idsubscriber){NSError*error;NSString*result=[NSStringstringWithContentsOfURL:[NSURLURLWithString:@"http://www.devtang.com"]encoding:NSUTF8StringEncodingerror:&error];NSLog(@"download");if(!result){[subscribersendError:error];}else{[subscribersendNext:result];[subscribersendCompleted];}return[RACDisposabledisposableWithBlock:^{NSLog(@"clean up");}];}];}

如何使用ReactiveCocoa

ReactiveCocoa可以在iOS和OS X的應用開發(fā)中使用,對于iOS開發(fā)者,可以將RAC源碼下載編譯后,使用編譯好的libReactiveCocoa-iOS.a文件。

開發(fā)者也可以用CocoaPods來設(shè)置目標工程對ReactiveCocoa的依賴,只需要編輯Podfile文件,增加如下內(nèi)容即可:

1

pod'ReactiveCocoa'

ReactiveCocoa的特點

RAC在應用中大量使用了block,由于Objective-C語言的內(nèi)存管理是基于引用計數(shù)的,為了避免循環(huán)引用問題,在block中如果要引用self,需要使用@weakify(self)和@strongify(self)來避免強引用。另外,在使用時應該注意block的嵌套層數(shù),不恰當?shù)臑E用多層嵌套block可能給程序的可維護性帶來災難。

RAC的編程方式和傳統(tǒng)的MVC方式差異巨大,所以需要較長的學習時間。并且,業(yè)界內(nèi)對于RAC并沒有廣泛應用,這造成可供參考的項目和教程比較欠缺。 另外,RAC項目本身也還在快速演進當中,1.x版本和2.x版本API改動了許多,3.0版本也正在快速開發(fā)中,對它的使用也需要考慮后期的升級維護問題。

作為一個iOS開發(fā)領(lǐng)域的新開源框架,ReactiveCocoa帶來了函數(shù)式編程和響應式編程的思想,值得大家關(guān)注并且學習。

一些學習資源

博客&教程

http://spin.atomicobject.com/2014/02/03/objective-c-delegate-pattern/

http://blog.bignerdranch.com/4549-data-driven-ios-development-reactivecocoa/

http://en.wikipedia.org/wiki/Functional_reactive_programming

http://www.teehanlax.com/blog/reactivecocoa/

http://www.teehanlax.com/blog/getting-started-with-reactivecocoa/

http://nshipster.com/reactivecocoa/

http://cocoasamurai.blogspot.com/2013/03/basic-mvvm-with-reactivecocoa.html

http://iiiyu.com/2013/09/11/learning-ios-notes-twenty-eight/

https://speakerdeck.com/andrewsardone/reactivecocoa-at-mobidevday-2013

http://msdn.microsoft.com/en-us/library/hh848246.aspx

http://www.itiger.me/?p=38

http://blog.leezhong.com/ios/2013/12/27/reactivecocoa-2.html

https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/FrameworkOverview.md

http://www.haskell.org/haskellwiki/Functional_Reactive_Programming

http://blog.zhaojie.me/2009/09/functional-reactive-programming-for-csharp.html

代碼

https://github.com/Machx/MVVM-IOS-Example

https://github.com/ReactiveCocoa/RACiOSDemo

書籍

https://leanpub.com/iosfrp

視頻

http://vimeo.com/65637501

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容