一篇關于RectiveCocoa的總結文檔
百度搜索了一下RectiveCocoa,都是與MVVM關聯在一起。
1·簡述下MVVM
MVC:ModelViewViewController
MVVM:ModelViewView-Model
Model:容納表現數據-模型對象信息的結構體,并在一個單獨的管理類中維護的創建/管理模型的統一邏輯。
View:包含實際 UI 本身(不論是 UIView 代碼, storyboard 和 xib), 任何視圖特定的邏輯, 和對用戶輸入的反饋. 在 iOS 中這不僅需要 UIView 代碼和那些文件, 還包括很多需由 UIViewController 處理的工作。
View-Model:
1、作為一個表現視圖顯示自身所需數據的靜態模型;
2、收集, 解釋和轉換那些數據;
收集:網絡請求,control控制的數值變更;
解釋:賦值;
轉換:viewmodel內的邏輯運算;
3、目的:讓view(controller)的任務更清晰,就是展示view-Model提供的數據。
Summary:Model不變,view不變,增加View-Model分擔ViewController的責任(每個view將有自己的view-model來約束)
其最終結果就是MVMCV
關于View-Model
不對視圖控制器起作用,或通告其變化
關于ViewController
1、不再發起網絡服務調用
2、不再管理數組(類似tableview的dataSource)
3、不再判斷某些值是否有效
4、其實就是不再管text或者image是什么了
5、只管view-Model中的變化
Summary:將view-model與view關聯,通過修改view model中的值的變更來修改view中的數值變化;ViewController就只能老老實實的管view-model的數值變更就好了。
2·ReactiveCocoa
解決View和View-Model關聯的問題
舉例
假設這是一個登陸頁面,我要判斷用戶名是否合法,密碼是否匹配,這些在MVC的情況下我都會在Controller中進行判斷,是的話會怎么怎么樣,不是的話會怎么怎么樣。
而在MVVM和RectiveCocoa的幫助下,我們可以使用一個登陸的ViewModel,所有的判斷和結果我可以寫在ViewModel中。而在Controller中我只要1、關聯用戶名的text和viewmodel中的username,密碼對應。2、就不用管TA了。
RectiveCocoa的學習
概念,一直是我不想去背卻又不得不背的東西。
1、RACSignal:RAC的構造單元,代表我們最終收到的信息,(當你能將未來某事某刻接收到的消息具體表示出來,我們可以預先運用邏輯構建信息流,而不是等到事件發生)
簡述:
1、信號是基本單位,也是寫完代碼會接收到的東西,因為是數據關聯在view和view-model上,我們可以提前將需要展示出來的數據在viewmodel中處理了,再通過關聯影響view的值的變化。而不是在controller中書寫了。(個人理解,如有偏差,請各種指正)
2、信號會為了控制通過應用的信息流而獲得所有這些異步方法(委托, 回調 block, 通知, KVO, target/action 事件觀察, 等)并將它們統一到一個接口下.這只是直觀理解. 不僅是這些, 因為信息會流過你的應用, 它還提供給你輕松轉換/分解/合并/過濾信息的能力.
簡述:中央集權制,信號中處理這些傳值方式;并通過書寫的邏輯達到預期的效果。
信號如何將ViewModel與View關聯上
信號是一個發送一連串值的物體。需要在訂閱者監聽時信號才會發信息,信號會向訂閱者發送0或多個帶有數值的’next’事件,后面帶有’complete’或’error’。
(信號類似于其他語言/工具包中的“promise”(1),但更強大,因為它不僅限于向它的訂閱者一次只傳遞一個返回值. )
1、創建一個信號
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
return nil;
}];
2、信號中添加訂閱者接收到的東西
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
//以下為新加
NSString *str=[NSString stringWithFormat:@"https://www.baidu.com"];
NSURL *url = [NSURL URLWithString:[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]init];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[subscriber sendNext:@""];
[subscriber sendCompleted];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[subscriber sendError:error];
}];
[operation start];
//end
return nil;
}];
3、RACObserve()
這個宏是 RAC 中對 KVO 中那些悲慘的 API 的替代。 你只需要傳入對象和你想觀察的那個對象某屬性的 keypath.。給出這些參數后, RACObserve 會創建一個信號,一旦它有了訂閱者,,它就立刻發送那個屬性的當前值,并在發送那個屬性在這之后的任何變化。
RACSignal *signal1 = RACObserve(self. personModel,name);
//這行代碼的前提是你有一個personModel,personModel有一個name屬性
這僅是提供用于創建信號的一個工具. 這里有幾個立即可用的方式, 來從內置控制流機制中拉取信號:
RACSignal *controlUpdate = [_myButtonrac_signalForControlEvents:UIControlEventTouchUpInside];
// signals for UIControl events send the control event value (UITextField, UIButton, UISlider, etc)
// subscribeNext:^(UIButton *button) { NSLog(@"%@", button); // UIButton instance }
RACSignal *textChange = [_myTextFieldrac_textSignal];
// some special methods are provided for commonly needed control event values off certain controls
// subscribeNext:^(UITextField *textfield) { NSLog(@"%@", _textfield.text); // "Hello!" }
RACSignal *alertButtonClicked = [_myAlertViewrac_buttonClickedSignal];
// signals for some delegate methods send the delegate params as the value
// e.g. UIAlertView, UIActionSheet, UIImagePickerControl, etc
// (limited to methods that return void)
// subscribeNext:^(NSNumber *buttonIndex) { NSLog(@"%@", buttonIndex); // "1" }
RACSignal *viewAppeared = [self rac_signalForSelector:@selector(viewDidAppear:)];
// signals for arbitrary selectors that return void, send the method params as the value
// works for built in or your own methods
// subscribeNext:^(NSNumber *animated) { NSLog(@"viewDidAppear %@", animated); // "viewDidAppear 1" }
什么是訂閱者?
簡言之,訂閱者就是一段代碼,它等待信號給它發送一些值,然后訂閱者就能處理這些值了. (它也可以作用于“complete”和“error”事件. )
簡述:理解為block,信號等同block調用?
RACSignal *usernameValidSignal = RACObserve(self. personModel,name);
// update the local property when this value changes
[usernameValidSignal subscribeNext: ^(NSNumber *isValidUserName) {
self.isValidName = isValidUserName.boolValue;
}];
以上代碼前提:controller 有一個personModel,有一個BOOL類型的isValidName,Model中有Number類型的isValidUserName
那問題來了,isValidName是BOOL為何我要傳NSNumber類型的。
答:RAC 只處理對象, 而不處理像 BOOL 這樣的原始值.。不過不用擔心,RAC 通常會幫你這些轉換。
RAC(self,isValidName) = RACObserve(self.personModel, isValidUserName);
//前面是target 后面是target下的@property
//用中文講,就是self.personModel的isValidUserName變化了,self.isValidName也要跟著變了。
那問題來了,self.isValidName做什么用,判斷嗎?那不就又繞回來了?把controller以前做判斷啊什么的邏輯交給viewmodel就好了,所以呢……
這樣我們可以把RACObserve(self.personModel, isValidUserName);綁定在textchange的協議方法中,或是確定按鈕的enable中,等等等等所有用這個值判斷的位置上(選位置時注意,不要總想在controller中操作)。
多個訂閱者, 副作用, 昂貴的操作
訂閱信號鏈時要明白重要的一件事是每當一個新值通過信號鏈被發送出去時, 實際上會給每個訂閱者都發送一次. 直到意識到這就我們而言是有意義的, 信號發出的值不存儲在任何地方(除了 RAC 在內部實現中). 當信號需要發送一個新的值時, 它會遍歷所有的訂閱者并給每個訂閱者發送那個值. (這是對信號鏈實際工作的簡化說明, 但基本想法是對的)
這為什么重要?這意味著信號鏈某處存在的任何副作用, 任何影響應用世界的轉變, 將會發生多次. 這對新接觸 RAC 的用戶來說是意想不到的. (這也違反了函數式構建的理念-數據輸入, 數據輸出).
一個做作的例子可能是: 信號鏈某處的信號在每次按鈕被按下時更新 self 中的一個計數器屬性. 如果信號鏈有多個訂閱者, 計數器的增長將會比你想的還要多. 你需要努力從信號鏈中盡可能剔除副作用. 當副作用不可避免時, 你可以使用一些恰當的預防機制. 我將會在另一篇文章中探索.
簡述:當存在多個訂閱者的時候,就像一對多廣播一樣,只要是訂閱者就會都遍歷一次。在設計之初就應該規范,不管是針對子viewModel的分別控制,還是條件限制,等等,
除副作用之外, 我們需要注意帶有昂貴操作和可變數據的信號鏈. 網絡請求就是一個三者兼得的例子:
網絡請求影響了應用的網絡層(副作用).
網絡請求為信號鏈引入了可變數據. (兩個完全一樣請求可能返回了不同的數據. )
網絡請求反應慢啊.
假設我根據一個text,text作為參數,發起多個網絡請求,我所要的結果在網絡請求的結果中,那么多個訂閱者如何處理這個返回結果,下一篇介紹RACCommand
額外:自行百度-mock、method swizzling
(1)promise的概念
ES6原生提供了Promise對象。
所謂Promise,就是一個對象,用來傳遞異步操作的消息。它代表了某個未來才會知道結果的事件(通常是一個異步操作),并且這個事件提供統一的API,可供進一步處理。