1. 程式人生 > >iOS開發之ReactiveCocoa下的MVVM(乾貨分享)

iOS開發之ReactiveCocoa下的MVVM(乾貨分享)

最近工作比較忙,但還是出來更新部落格了,今天給大家分享一些ReactiveCocoa以及MVVM的一些東西,幹活還是比較足的。在之前發表過一篇博文,名字叫做,大體上講的就是使用Block回撥的方式實現MVVM的。在寫上篇文章時也知道有ReactiveCocoa這個函式響應式程式設計的框架,並且有許多人用它來更好的實現MVVM。所以在上篇部落格發表後,有些同行給評論建議看一下ReactiveCocoa的東西,所以就係統的看了一下ReactiveCocoa的東西。不過有一點要說明的就是,不使用ReactiveCocoa是可以實現MVVM的,並非使用MVVM模式你就必須的使用ReactiveCocoa的東西,你可以使用KVO,Block,Delegate,Navigation等手段,而ReactiveCocoa更優雅的實現了這個過程。ReactiveCocoa就是一個響應式程式設計的框架,它會使MVVM每層之間互動起來更為方便,所以長和MVVM聯絡在一起。

一.函式響應式程式設計(Function Reactive Programming)

關於函式響應式程式設計的東西,我想引用國外這個ReactiveCocoa教學視訊(視訊連結)中的一張PPT來簡單的說一下什麼是函式響應式程式設計。那就直接上圖,下圖是上方視訊連結的截圖,很形象的解釋了什麼是函式響應式程式設計。簡單的說下方c = a + b 定義好後,當a的值變化後,c的值就會自動變化。不過a的值變化時會產生一個訊號,這個訊號會通知c根據a變化的值來變化自己的值。b的值變化同樣也影響c的值。下圖很好的表達了這個思想。在此就不做贅述了。

二. ReactiveCocoa簡介

先簡單的介紹一下什麼是ReactiveCocoa框架,然後通過例項好好的去搞一搞這個框架,最後就是如何在專案中使用了。關於ReactiveCocoa的理解一些部落格(見本篇部落格中的連結分享)中把ReactiveCocoa比喻成管道,ReactiveCocoa中的Signal就是管道中的水流。使用ReactiveCocoa可以方便的在MVVM各層之間架起溝通的管道,便於每層之間的互動。現在在我們做的工程中已經在使用ReactiveCocoa框架了,用起來的感覺是非常爽的,好用!

可以說ReactiveCocoa中核心是訊號量機制,Signal在ReactiveCocoa中發揮著強大的不可代替的作用,可謂是ReactiveCocoa的靈魂所。Signal是可以攜帶一些物件和引數的,你可以獲取該物件並且可以對該訊號量攜帶的值進行map, filter等常用操作,操作後的值會和該訊號量進行繫結。先簡單的這麼一說,後邊的部分回詳細的介紹如何讓訊號量發揮強大的作用。

ReactiveCocoa中對Block的使用可謂是淋漓盡致,如果對Block使用不熟的朋友可以補一下Block的東西,然後在回頭看一下ReactiveCocoa的東西。關於ReactiveCocoa更多的東西,請參考Github上的連結()。

三. 在工程中引入ReactiveCocoa

1.你可以使用Github上的加入方式如下所示,本人感覺比較麻煩,就沒有使用,採用的第二種方法(CocoaPods)。

2.上面的步驟難免有些麻煩,所以用CocoaPods更為便捷一些,Profile檔案中的內容如下所示,我用的是2.5版本。3.0後就支援Swift了,因為我沒有用Swift寫東西,所以就用的是2.5版本,設定完Profile檔案後,pod install即可。

你可以pod search ReactiveCocoa看一下版本,選擇你需要的版本即可。

四.使用ReactiveCocoa

下方會通過一些簡單的例項來介紹一下訊號量機制和一些常用的方法。

1.引入相應的標頭檔案

在工程中引入下方的標頭檔案(建議在Pch檔案中引入)就可以使用我們的ReactiveCocoa框架了

Objective-C
12 #import <ReactiveCocoa/ReactiveCocoa.h>#import <ReactiveCocoa/RACEXTScope.h>

2. Sequence和Map

Sequence:佇列,是ReactiveCocoa中引入的一個型別,它類似於陣列,我們可以暫且把Sequence看做繫結訊號量的陣列吧。在OC中的NSArray可以通過rac_sequence方法轉換成ReactiveCocoa中的Sequence,然後就可以呼叫處理訊號的一些方法了。

參考以下例項程式碼:

(1)把NSArray通過rac_sequence方法生成RAC中的Sequence

(2)獲取該Sequence物件的訊號量

(3)呼叫Signal的Map方法,使每個元素的首字母大寫

(4)通過subscribNext方法對其進行遍歷輸出

Objective-C
12345678910111213141516171819 //uppercaseString use map-(void)uppercaseString{RACSequence*sequence=[@[@"you",@"are",@"beautiful"] rac_sequence];RACSignal*signal=sequence.signal;RACSignal*capitalizedSignal=[signal map:^id(NSString*value){return[value capitalizedString];}];[signal subscribeNext:^(NSString*x){NSLog(@"signal --- %@",x);}];[capitalizedSignal subscribeNext:^(NSString*x){NSLog(@"capitalizedSignal --- %@",x);}];}

下方截圖是上個這個方法中的執行結果,從執行結果不難看出,通過Signal相應的方法處理完後,處理的結果會與新返回的訊號量所繫結。原訊號量中的值保持不變。每次訊號量呼叫相應的方法處理完資料後,都會返回一個新的訊號量,而這個訊號量是獨立於原訊號量的。

由上面的介紹可知,上面方法中的一坨程式碼可以寫成下方的一串。因為一個方法呼叫後會返回一個持有新結果的新的訊號量,然後在這個訊號量的基礎上再次呼叫訊號量其他的方法。Signal還有其他一些好用的方法,用法和map方法類似,在此就不一一贅述了,gitHub上有相應的例項文件。

Objective-C
123456789 -(void)uppercaseString{[[[@[@"you",@"are",@"beautiful"] rac_sequence].signal      map:^id(NSString*value){return[value capitalizedString];}] subscribeNext:^(idx){NSLog(@"capitalizedSignal --- %@",x);}];}

3.訊號量開關(Switch)

上面把訊號量比喻成水管,那麼Switch就是水龍頭呢。通過Switch我們可以控制那個訊號量起作用,並且可以在訊號量之間進行切換。也可以這麼理解,把Switch看成另一段水管,Switch對接那個水管,就流那個水管的水,這樣比喻應該更為貼切一些。下方是一個關於Switch的一個小例項。

(1) 首先建立3個自定義訊號量(3個水管),前兩個水管是用來接通不同的水源的(google, baidu), 而最後一個訊號量是用來對接不同水源水管的水管(signalOfSignal)。signalOfSignal接baidu水管上,他就流baidu水源的水,接google水管上就流google水源的水。

(2) 把signalOfSignal訊號量通過switchToLatest方法加工成開關訊號量。

(3) 緊接著是對通過開關資料進行處理。

(4) 開關對接baidu訊號量,然後baidu和google訊號量同時往水管裡灌入資料,那麼起作用的是baidu訊號量。

(5) 開關對接google訊號量,google和baidu訊號量傳送資料,則google訊號量輸出到signalOfSignal中

Objective-C
123456789101112131415161718192021222324252627 //訊號開關Switch-(void)signalSwitch{//建立3個自定義訊號RACSubject*google=[RACSubjectsubject];RACSubject*baidu=[RACSubjectsubject];RACSubject*signalOfSignal=[RACSubjectsubject];//獲取開關訊號RACSignal*switchSignal=[signalOfSignal switchToLatest];//對通過開關的訊號量進行操作[[switchSignal  map:^id(NSString*value){return[@"https//www." stringByAppendingFormat:@"%@",value];}] subscribeNext:^(NSString*x){NSLog(@"%@",x);}];//通過開關開啟baidu[signalOfSignal sendNext:baidu];[baidu sendNext:@"baidu.com"];[google sendNext:@"google.com"];//通過開關開啟google[signalOfSignal sendNext:google];[baidu sendNext:@"baidu.com/"];[google sendNext:@"google.com/"];}

上面程式碼輸出結果如下:

4.訊號量的合併

訊號量的合併說白了就是把兩個水管中的水合成一個水管中的水。但這個合併有個限制,當兩個水管中都有水的時候才合併。如果一個水管中有水,另一個水管中沒有水,那麼有水的水管會等到無水的水管中來水了,在與這個水管中的水按規則進行合併。下面這個例項就是把兩個訊號量進行合併。

(1) 首先建立兩個自定義的訊號量letters和numbers

(2) 吧兩個訊號量通過combineLatest函式進行合併,combineLatest說明要合併訊號量中最後傳送的值

(3) reduce塊中是合併規則:把numbers中的值拼接到letters訊號量中的值後邊。

(4) 經過上面的步驟就是建立所需的相關訊號量,也就是相當於架好運輸的管道。接著我們就可以通過sendNext方法來往訊號量中傳送值了,也就是往管道中進行灌水。

Objective-C
123456789101112131415161718192021 //組合訊號-(void)combiningLatest{RACSubject*letters=[RACSubjectsubject];RACSubject*numbers=[RACSubjectsubject];[[RACSignal       combineLatest:@[letters,numbers]       reduce:^(NSString*letter,NSString*number){return[letter stringByAppendingString:number];}]      subscribeNext:^(NSString*x){NSLog(@"%@",x);}];//B1 C1 C2[letters sendNext:@"A"];[letters sendNext:@"B"];[numbers sendNext:@"1"];[letters sendNext:@"C"];[numbers sendNext:@"2"];}

上面示例的執行輸出結果如下:

下面是自己畫的原理圖,思路應該還算是清晰。

5.訊號的合併(merge)

訊號合併就理解起來就比較簡單了,merge訊號量規則比較簡單,就是把多個訊號量,放入陣列中通過merge函式來合併陣列中的所有訊號量為一個。類比一下,合併後,無論哪個水管中有水都會在merge產生的水管中流出來的。下方是merge訊號量的程式碼:

(1) 建立三個自定義訊號量, 用於merge

(2) 合併上面建立的3個訊號量

(3) 往訊號裡灌入資料

Objective-C
12345678910111213141516 //合併訊號-(void)merge{RACSubject*letters=[RACSubjectsubject];RACSubject*numbers=[RACSubjectsubject];RACSubject*chinese=[RACSubjectsubject];[[RACSignal       merge:@[letters,numbers,chinese]]       subscribeNext:^(idx){NSLog(@"merge:%@",x);}];[letters sendNext:@"AAA"];[numbers sendNext:@"666"];[chinese sendNext:@"你好!"];}

上面程式碼執行結果如下:

上面示例的原理圖如下:

五. 在MVVM中引入RactiveCocoa

學以致用,最後來個簡單的例項,來感受一下如何在MVVM中使用RactiveCocoa。當然今天RAC的應用是非常簡單的,但原理就是這樣的。接下啦我們要使用RAC模擬一下登入功能,當然,網路請求也是模擬的,這不是重點。重點在於如何在MVVM各層之間使用RAC的訊號量來更方便的在各個層之間進行響應式資料互動。下面這個例項的UI是非常簡單的,並且實現起來也是灰常簡單的,關鍵還是在於RAC的應用。

1.搭建Demo所需UI,使用者介面非常簡單,公有兩個使用者介面,一個是登入頁面(兩個輸入框,一個登入按鈕),一個是登入後跳轉的頁面(一個展示使用者名稱和密碼的Label)。下方是使用Storyboard實現的使用者登入頁面。實現完後,個兩個頁面各自關聯一個ViewContorller類。

2.下方是整個小Demo的工程目錄,因為我們今天的重點是如何在MVVM中使用RAC, 所以重點在於RAC的應用,對於MVVM的分層就簡化一些。下方有VC層,在VC層中有兩個檢視控制器,一個是登入使用的檢視控制器(ViewContorller)另一個是登入成功後的檢視控制器(LoginSuccessViewController)。而ViewModel中則是負責登入的ViewModel業務邏輯層,該層中負責資料驗證,網路請求,資料儲存等一些與UI無關的業務邏輯。

3.實現登入的ViewModel層

因為ViewModel層是獨立於UI層而存在的,所以可以在沒有UI的情況下我們就可以去實現相應模組的ViewModel層。這正好減少了個個層次間的耦合性,同時也提高了可測試性,總體上改善了可維護性。好廢話少說,接下來要實現登入的ViewModel層。

(1) 登入ViewModel層對應的類的標頭檔案中的內容如下所示(VCViewModel.h), 其實下方一些常用的訊號量可以抽象出來放到ViewModel的父類中,這為了簡化Demo沒有做父類的抽象。下方就是VCViewModel中interface定義的公有屬性和公有方法(Public)。userName和password(NSString型別) 用來繫結使用者輸入的使用者名稱和密碼。下方三個自定義訊號量successObject, failureObject, errorObject 用來發送網路請求的資料。successObject負責處理網路請求成功且符合正常業務邏輯的事件, failureObject負責網路請求成功不符合正常業務邏輯的處理,errorObject負責網路異常處理。

Objective-C
1234567891011121314151617181920 ////  VCViewModel.h//  ReactiveCocoaDemo////  Created by Mr.LuDashi on 15/10/19.//  Copyright © 2015年 ZeluLi. All rights reserved.//#import <Foundation/Foundation.h>@interface VCViewModel : NSObject@property(nonatomic,strong)NSString*userName;@property(nonatomic,strong)NSString*password;@property(nonatomic,strong)RACSubject*successObject;@property(nonatomic,strong)RACSubject*failureObject;@property(nonatomic,strong)RACSubject*errorObject;-(id)buttonIsValid;-(void)login;@end

上面可能說的有些抽象,結合專案中的例項來解釋一下什麼時候傳送successObject訊號量,如何傳送failureObject訊號量,何時使用errorObject訊號量。

以某些理財App中購買理財產品的業務流程為例。在使用者下單之前先去判斷使用者是否實名認證以及繫結銀行卡,如果使用者已經實名和繫結銀行卡就走正常支付流程(使用者就是想去下單購買),VM就往VC傳送successObject訊號,當前VC就會根據訊號量的指示跳轉到下單支付頁面。  但是如果使用者沒有實名或者綁卡,那麼VM就給VC傳送failureObject訊號,根據訊號量中的引數來判斷是走實名認證流程還是走繫結銀行卡流程。 errorObject就比較簡單了,網路異常,後臺伺服器丟擲的異常等不需要iOS這邊做業務邏輯處理的,就放在errorObject中負責錯誤資訊的展示。

文字說完了,如果有些小夥伴還不太明白,那看下面這張原理圖吧。把三種訊號量我們可以類比成十字路口的紅綠燈。successObject就是綠燈,可以走正常流程。failureObject是黃燈,先等一下,完成該做的就可以走綠燈了。而errorObject就是一紅燈,報錯異常,終止業務流程並提升錯誤資訊。有圖有真相,到這兒如果還不理解我就沒招了。

在Public方法中– (id) buttonIsValid; 負責返回登入按鈕是否可用的訊號量。– (void)login;發起網路請求,呼叫登入網路介面。

(2)程式碼的具體實現如下(VCViewModel.m中的程式碼),私有屬性如下。userNameSignal用來儲存使用者名稱的訊號量,passwordSignal是用來儲存密碼的訊號量。reqestData則是用來儲存返回資料的。

相關推薦

iOS開發ReactiveCocoaMVVM乾貨分享

最近工作比較忙,但還是出來更新部落格了,今天給大家分享一些ReactiveCocoa以及MVVM的一些東西,幹活還是比較足的。在之前發表過一篇博文,名字叫做,大體上講的就是使用Block回撥的方式實現MVVM的。在寫上篇文章時也知道有ReactiveCocoa這個函式響應式程式

iOS開發ReactiveCocoaMVVM

最近工作比較忙,但還是出來更新部落格了,今天給大家分享一些ReactiveCocoa以及MVVM的一些東西,幹活還是比較足的。在之前發表過一篇博文,名字叫做,大體上講的就是使用Block回撥的方式實現MVVM的。在寫上篇文章時也知道有ReactiveCocoa這個函式響應式程式設計的框架,並且有許多人用它來更

iOS開發執行時程式設計Runtime Programming淺讀

  什麼是執行時(Objective-C runtime)?       簡單的來說,Objective-C runtime是一個實現 Objective-C語言的庫。物件可以用C語言的結構體表示,而方法(methods) 可以用C函式實現。       事實上,他們也差不

ios開發--UIDocumentInteractionController的使用實現更多分享服務

void cnblogs 實例 內容 main 華麗 例如 一個 img 最近在做項目的時候,碰到這樣一個需求,就是本地生成pdf文件,然後本地打開,經過測試發現,pdf文件是無法保存到相冊裏面的,只能存到手機裏面,鑒於蘋果的存儲機制,需要取出來,進行本地展示,可以直接傳到

iOS開發JSON轉PLIST把存儲json格式的文件轉換成plist文件

string 數據 導致 atom use error: ali ror 進行 有時開發過程中,經常需要調試接口,但是可能經常沒有網絡,導致調試無法正常進行。 對此可以自己手動設置一些假數據,也可以通過計算機來為我們保存一份真實的網絡數據,並自己轉化成plist數據,

iOS開發淺談MVVM的架構設計與團隊協作

1 // 2 // NetRequestClass.m 3 // MVVMTest 4 // 5 // Created by 李澤魯 on 15/1/6. 6 // Copyright (c) 2015年 李澤魯. All rights reserved. 7

iOS開發效能除錯Instruments

如何定位記憶體問題 今天主要講最常見的定位記憶體問題,普遍使用ARC後,開發者們從手動管理引用計數中解放出來,但開啟了ARC並不是就不會存在記憶體問題。 蘋果有句名言:ARC is only for NSObject。在iOS 中使用malloc分配的記憶體,ARC是不會

iOS開發效能除錯Instruments

iOS效能除錯有很多方法,這裡講一下Xcode內建工具Instruments。 Instruments是一個官方提供的強大的效能除錯工具集。 instruments.png 1.Blank(空模板):建立一個空的模板,可以從Library庫中新增其他模板; 2.Act

iOS開發自定義鍵盤數字,字母型別等隨意切換

專案開發很多時候用系統給的鍵盤不是很滿足自身實際需求,那就自定義一個吧: 方法其實很簡單,重新定義一個view,繼承UItextfield,把UI設計好的需求鍵盤加入新的otherKeyboardView,然後執行程式碼:  self.inputView =self.oth

IOS開發XcodeLLDB除錯技巧_Debug_更改BOOL型別的值

1.當在除錯中更改bool型別的值時,不能用NO和YES,要用true和false 2.如果有個字典userInfoDic {     avatar = "",     birthday = "

Cocos2d—X遊戲開發CCTableView詳解十一附原始碼

本來很早就想寫關於CCTableView的文章,但是在基本功能實現之後呢,專案需求增加導致對這個控制元件的研究必須更加深入一點. 好的,現在開始介紹一下這個控制元件,在Cocos2d—X引擎中,這是一個仿製iOS裡面的NSTableView的一個控制元件。 S1,使用這個

小程式開發檔案作用域全域性變數與模組化utils抽離工具類

檔案作用域 在 JavaScript 檔案中宣告的變數和函式只在該檔案中有效;不同的檔案中可以宣告相同名字的變數和函式,不會互相影響。 通過全域性函式 getApp() 可以獲取全域性的應用例項,如果需要全域性的資料可以在 App() 中設定, 例如: glob

iOS開發Xcode的圖片管理Images.xcassets

一、首先,在iOS來法中使用的圖片格式主要是JPG和PNG兩種格式         (1)JPG: 壓縮比比較高,通常用於照片、網頁,屬於有失真壓縮(噪點)。解壓縮時,對CPU消耗大,意味慢,費電。          (2)PNG:壓縮比較高,無失真壓縮,  解壓縮效率高,

微信小程式開發表單驗證WxValidate使用

微信小程式的開發框架個人感覺大體上跟VUE是差不多的,但是他的表單元件沒有自帶的驗證功能,因此開發小程式的表單驗證時候一般有兩種方法,一是自己裸寫驗證規則,但是需要比較紮實的正則表示式基礎,一種是利用官方社群開發的WxValidate外掛進行表單驗證。 WxValidat

iOS開發UI篇—Quartz2D使用截屏

gin 上下文 title 核心 tor void 寫入 常見 import 一、簡單說明 在程序開發中,有時候需要截取屏幕上的某一塊內容,比如捕魚達人遊戲。如圖: 完成截屏功能的核心代碼:- (void)renderInContext:(CGContextRef)ct

iOS開發UI篇—Quartz2D使用矩陣操作

兩個 四邊形 圖形 代碼示例 stroke beyond rec 代碼 graphics iOS開發UI篇—Quartz2D使用(矩陣操作) 一、關於矩陣操作 1.畫一個四邊形 通過設置兩個端點(長和寬)來完成一個四邊形的繪制。 代碼: 1 - (void)dr

Android開發拍照功能實現附原始碼

    大家好,這是一個簡單的拍照功能,很簡單的介面,一個顯示影象區域SurfaceView一個“拍照”按鈕。直接上程式碼! 1、CameraDemoActivity.java(主介面) package org.winplus.camera; import java.i

乾貨分享mac python+appium環境搭建

因為mac本自帶python2.x(不建議解除安裝,因為本本本身有很多依賴與此),所以裝python3的過程極其坎坷,勉強裝好後也總是各種報錯。這次裝appium環境,直接把原來的python3卸了,用homebrew安裝。建議大家也用這個安裝吧,簡單而且錯誤率低。 --brew的安裝命令: /usr/bin

ios開發--textview意見反饋頁面占位label,字數統計,提交按鈕的交互設置

ould 技術 ima out == img ios開發 del 分享圖片 記錄一個頁面的功能: textview的占位符,字數統計,提交按鈕的交互設置,具體效果圖如下: 輸入效果: 具體實現代碼如下: 1,設置代理 @interface FKViewControll

ios開發--仿微信自定義表情鍵盤

lai signed avi 創建 不能 url div load mps 先附上demo:https://github.com/hgl753951/CusEmoji.git 效果圖如下: 先說下具體的實現功能: 1,本地加載了一些H5的代碼,直接使用webview的lo