一篇關(guān)于RectiveCocoa的總結(jié)文檔
百度搜索了一下RectiveCocoa告匠,都是與MVVM關(guān)聯(lián)在一起太援。
1·簡述下MVVM
MVC:ModelViewViewController
MVVM:ModelViewView-Model
Model:容納表現(xiàn)數(shù)據(jù)-模型對象信息的結(jié)構(gòu)體,并在一個單獨的管理類中維護(hù)的創(chuàng)建/管理模型的統(tǒng)一邏輯愉粤。
View:包含實際 UI 本身(不論是 UIView 代碼, storyboard 和 xib), 任何視圖特定的邏輯, 和對用戶輸入的反饋. 在 iOS 中這不僅需要 UIView 代碼和那些文件, 還包括很多需由 UIViewController 處理的工作询件。
View-Model:
1缚忧、作為一個表現(xiàn)視圖顯示自身所需數(shù)據(jù)的靜態(tài)模型僵控;
2痕钢、收集, 解釋和轉(zhuǎn)換那些數(shù)據(jù)图柏;
收集:網(wǎng)絡(luò)請求,control控制的數(shù)值變更任连;
解釋:賦值爆办;
轉(zhuǎn)換:viewmodel內(nèi)的邏輯運算;
3课梳、目的:讓view(controller)的任務(wù)更清晰距辆,就是展示view-Model提供的數(shù)據(jù)。
Summary:Model不變暮刃,view不變跨算,增加View-Model分擔(dān)ViewController的責(zé)任(每個view將有自己的view-model來約束)
其最終結(jié)果就是MVMCV
關(guān)于View-Model
不對視圖控制器起作用,或通告其變化
關(guān)于ViewController
1椭懊、不再發(fā)起網(wǎng)絡(luò)服務(wù)調(diào)用
2诸蚕、不再管理數(shù)組(類似tableview的dataSource)
3、不再判斷某些值是否有效
4氧猬、其實就是不再管text或者image是什么了
5背犯、只管view-Model中的變化
Summary:將view-model與view關(guān)聯(lián),通過修改view model中的值的變更來修改view中的數(shù)值變化盅抚;ViewController就只能老老實實的管view-model的數(shù)值變更就好了漠魏。
2·ReactiveCocoa
解決View和View-Model關(guān)聯(lián)的問題
舉例
假設(shè)這是一個登陸頁面,我要判斷用戶名是否合法妄均,密碼是否匹配柱锹,這些在MVC的情況下我都會在Controller中進(jìn)行判斷哪自,是的話會怎么怎么樣,不是的話會怎么怎么樣禁熏。
而在MVVM和RectiveCocoa的幫助下壤巷,我們可以使用一個登陸的ViewModel,所有的判斷和結(jié)果我可以寫在ViewModel中瞧毙。而在Controller中我只要1胧华、關(guān)聯(lián)用戶名的text和viewmodel中的username,密碼對應(yīng)宙彪。2矩动、就不用管TA了。
RectiveCocoa的學(xué)習(xí)
概念您访,一直是我不想去背卻又不得不背的東西铅忿。
1剪决、RACSignal:RAC的構(gòu)造單元灵汪,代表我們最終收到的信息,(當(dāng)你能將未來某事某刻接收到的消息具體表示出來柑潦,我們可以預(yù)先運用邏輯構(gòu)建信息流享言,而不是等到事件發(fā)生)
簡述:
1、信號是基本單位渗鬼,也是寫完代碼會接收到的東西览露,因為是數(shù)據(jù)關(guān)聯(lián)在view和view-model上,我們可以提前將需要展示出來的數(shù)據(jù)在viewmodel中處理了譬胎,再通過關(guān)聯(lián)影響view的值的變化差牛。而不是在controller中書寫了。(個人理解堰乔,如有偏差偏化,請各種指正)
2、信號會為了控制通過應(yīng)用的信息流而獲得所有這些異步方法(委托, 回調(diào) block, 通知, KVO, target/action 事件觀察, 等)并將它們統(tǒng)一到一個接口下.這只是直觀理解. 不僅是這些, 因為信息會流過你的應(yīng)用, 它還提供給你輕松轉(zhuǎn)換/分解/合并/過濾信息的能力.
簡述:中央集權(quán)制镐侯,信號中處理這些傳值方式侦讨;并通過書寫的邏輯達(dá)到預(yù)期的效果。
信號如何將ViewModel與View關(guān)聯(lián)上
信號是一個發(fā)送一連串值的物體苟翻。需要在訂閱者監(jiān)聽時信號才會發(fā)信息韵卤,信號會向訂閱者發(fā)送0或多個帶有數(shù)值的’next’事件,后面帶有’complete’或’error’崇猫。
(信號類似于其他語言/工具包中的“promise”(1),但更強大,因為它不僅限于向它的訂閱者一次只傳遞一個返回值. )
1沈条、創(chuàng)建一個信號
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.贴谎。給出這些參數(shù)后, RACObserve 會創(chuàng)建一個信號,一旦它有了訂閱者,季稳,它就立刻發(fā)送那個屬性的當(dāng)前值擅这,并在發(fā)送那個屬性在這之后的任何變化。
RACSignal *signal1 = RACObserve(self. personModel,name);
//這行代碼的前提是你有一個personModel景鼠,personModel有一個name屬性
這僅是提供用于創(chuàng)建信號的一個工具. 這里有幾個立即可用的方式, 來從內(nèi)置控制流機制中拉取信號:
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" }
什么是訂閱者?
簡言之,訂閱者就是一段代碼,它等待信號給它發(fā)送一些值,然后訂閱者就能處理這些值了. (它也可以作用于“complete”和“error”事件. )
簡述:理解為block仲翎,信號等同block調(diào)用?
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 這樣的原始值.。不過不用擔(dān)心包晰,RAC 通常會幫你這些轉(zhuǎn)換湿镀。
RAC(self,isValidName) = RACObserve(self.personModel, isValidUserName);
//前面是target 后面是target下的@property
//用中文講,就是self.personModel的isValidUserName變化了伐憾,self.isValidName也要跟著變了勉痴。
那問題來了,self.isValidName做什么用树肃,判斷嗎蒸矛?那不就又繞回來了?把controller以前做判斷啊什么的邏輯交給viewmodel就好了胸嘴,所以呢……
這樣我們可以把RACObserve(self.personModel, isValidUserName);綁定在textchange的協(xié)議方法中雏掠,或是確定按鈕的enable中,等等等等所有用這個值判斷的位置上(選位置時注意劣像,不要總想在controller中操作)乡话。
多個訂閱者, 副作用, 昂貴的操作
訂閱信號鏈時要明白重要的一件事是每當(dāng)一個新值通過信號鏈被發(fā)送出去時, 實際上會給每個訂閱者都發(fā)送一次. 直到意識到這就我們而言是有意義的, 信號發(fā)出的值不存儲在任何地方(除了 RAC 在內(nèi)部實現(xiàn)中). 當(dāng)信號需要發(fā)送一個新的值時, 它會遍歷所有的訂閱者并給每個訂閱者發(fā)送那個值. (這是對信號鏈實際工作的簡化說明, 但基本想法是對的)
這為什么重要?這意味著信號鏈某處存在的任何副作用, 任何影響應(yīng)用世界的轉(zhuǎn)變, 將會發(fā)生多次. 這對新接觸 RAC 的用戶來說是意想不到的. (這也違反了函數(shù)式構(gòu)建的理念-數(shù)據(jù)輸入, 數(shù)據(jù)輸出).
一個做作的例子可能是: 信號鏈某處的信號在每次按鈕被按下時更新 self 中的一個計數(shù)器屬性. 如果信號鏈有多個訂閱者, 計數(shù)器的增長將會比你想的還要多. 你需要努力從信號鏈中盡可能剔除副作用. 當(dāng)副作用不可避免時, 你可以使用一些恰當(dāng)?shù)念A(yù)防機制. 我將會在另一篇文章中探索.
簡述:當(dāng)存在多個訂閱者的時候,就像一對多廣播一樣驾讲,只要是訂閱者就會都遍歷一次蚊伞。在設(shè)計之初就應(yīng)該規(guī)范,不管是針對子viewModel的分別控制吮铭,還是條件限制时迫,等等,
除副作用之外, 我們需要注意帶有昂貴操作和可變數(shù)據(jù)的信號鏈. 網(wǎng)絡(luò)請求就是一個三者兼得的例子:
網(wǎng)絡(luò)請求影響了應(yīng)用的網(wǎng)絡(luò)層(副作用).
網(wǎng)絡(luò)請求為信號鏈引入了可變數(shù)據(jù). (兩個完全一樣請求可能返回了不同的數(shù)據(jù). )
網(wǎng)絡(luò)請求反應(yīng)慢啊.
假設(shè)我根據(jù)一個text谓晌,text作為參數(shù)掠拳,發(fā)起多個網(wǎng)絡(luò)請求,我所要的結(jié)果在網(wǎng)絡(luò)請求的結(jié)果中纸肉,那么多個訂閱者如何處理這個返回結(jié)果溺欧,下一篇介紹RACCommand
額外:自行百度-mock喊熟、method swizzling
(1)promise的概念
ES6原生提供了Promise對象。
所謂Promise姐刁,就是一個對象芥牌,用來傳遞異步操作的消息。它代表了某個未來才會知道結(jié)果的事件(通常是一個異步操作)聂使,并且這個事件提供統(tǒng)一的API壁拉,可供進(jìn)一步處理。