iOS應(yīng)用架構(gòu)談(二):View層的組織和調(diào)用方案(中)

轉(zhuǎn)載 :http://casatwy.com/iosying-yong-jia-gou-tan-kai-pian.html

OS客戶端應(yīng)用架構(gòu)看似簡單,但實(shí)際上要考慮的事情不少。本文作者將以系列文章的形式來回答iOS應(yīng)用架構(gòu)中的種種問題,本文是其中的第二篇,主要講View層的組織和調(diào)用方案斩箫。中篇主要討論MVC、MVCS、MVVM蔗衡、VIPER等架構(gòu)在iOS開發(fā)中的應(yīng)用。

關(guān)于MVC乳绕、MVVM等一大堆思想

其實(shí)這些都是相對通用的思想绞惦,萬變不離其宗的還是在開篇里面我提到的那三個角色:數(shù)據(jù)管理者,數(shù)據(jù)加工者洋措,數(shù)據(jù)展示者济蝉。這些五花八門的思想,不外乎就是制訂了一個規(guī)范菠发,規(guī)定了這三個角色應(yīng)當(dāng)如何進(jìn)行數(shù)據(jù)交換王滤。但同時這些也是爭議最多的話題,所以我在這里來把幾個主流思想做一個梳理滓鸠,當(dāng)你在做View層架構(gòu)時雁乡,能夠有個比較好的參考。

MVC

MVC(Model-View-Controller)是最老牌的的思想糜俗,老牌到4人幫的書里把它歸成了一種模式踱稍,其中Model就是作為數(shù)據(jù)管理者墩弯,View作為數(shù)據(jù)展示者,Controller作為數(shù)據(jù)加工者寞射,Model和View又都是由Controller來根據(jù)業(yè)務(wù)需求調(diào)配渔工,所以Controller還負(fù)擔(dān)了一個數(shù)據(jù)流調(diào)配的功能。正在我寫這篇文章的時候桥温,我看到InfoQ發(fā)了這篇文章引矩,里面提到了一個移動開發(fā)中的痛點(diǎn)是:對MVC架構(gòu)劃分的理解。我當(dāng)時沒能夠去參加這個座談會侵浸,也沒辦法發(fā)表個人意見旺韭,所以就只能在這里寫寫了。

在iOS開發(fā)領(lǐng)域掏觉,我們應(yīng)當(dāng)如何進(jìn)行MVC的劃分区端?

這里面其實(shí)有兩個問題:

為什么我們會糾結(jié)于iOS開發(fā)領(lǐng)域中MVC的劃分問題?

在iOS開發(fā)領(lǐng)域中澳腹,怎樣才算是劃分的正確姿勢织盼?

為什么我們會糾結(jié)于iOS開發(fā)領(lǐng)域中MVC的劃分問題?

關(guān)于這個酱塔,每個人糾結(jié)的點(diǎn)可能不太一樣沥邻,我也不知道當(dāng)時座談會上大家的觀點(diǎn)。但請?jiān)试S我猜一下:是不是因?yàn)閁IViewController中自帶了一個View羊娃,且控制了View的整個生命周期(viewDidLoad,viewWillAppear...)唐全,而在常識中我們都知道Controller不應(yīng)該和View有如此緊密的聯(lián)系,所以才導(dǎo)致大家對劃分產(chǎn)生困惑蕊玷?邮利,下面我會針對這個猜測來給出我的意見。

在服務(wù)端開發(fā)領(lǐng)域垃帅,Controller和View的交互方式一般都是這樣延届,比如Yii:

/*

...

數(shù)據(jù)庫取數(shù)據(jù)

...

處理數(shù)據(jù)

...

*/

// 此處$this就是Controller

$this->render("plan",array(

'planList' => $planList,

'plan_id' => $_GET['id'],

));

這里Controller和View之間區(qū)分得非常明顯,Controller做完自己的事情之后挺智,就把所有關(guān)于View的工作交給了頁面渲染引擎去做祷愉,Controller不會去做任何關(guān)于View的事情,包括生成View赦颇,這些都由渲染引擎代勞了二鳄。這是一個區(qū)別,但其實(shí)服務(wù)端View的概念和Native應(yīng)用View的概念媒怯,真正的區(qū)別在于:從概念上嚴(yán)格劃分的話订讼,服務(wù)端其實(shí)根本沒有View,拜HTTP協(xié)議所賜扇苞,我們平時所討論的View只是用于描述View的字符串(更實(shí)質(zhì)的應(yīng)該稱之為數(shù)據(jù))欺殿,真正的View是瀏覽器寄纵。。

所以服務(wù)端只管生成對View的描述脖苏,至于對View的長相程拭,UI事件監(jiān)聽和處理,都是瀏覽器負(fù)責(zé)生成和維護(hù)的棍潘。但是在Native這邊來看恃鞋,原本屬于瀏覽器的任務(wù)也逃不掉要自己做。那么這件事情由誰來做最合適亦歉?蘋果給出的答案是:UIViewController恤浪。

鑒于蘋果在這一層做了很多艱苦卓絕的努力,讓iOS工程師們不必親自去實(shí)現(xiàn)這些內(nèi)容肴楷。而且水由,它把所有的功能都放在了UIView上,并且把UIView做成不光可以展示UI赛蔫,還可以作為容器的一個對象砂客。

看到這兒你明白了嗎?UIView的另一個身份其實(shí)是容器濒募!UIViewController中自帶的那個view鞭盟,它的主要任務(wù)就是作為一個容器。如果它所有的相關(guān)命名都改成ViewContainer瑰剃,那么代碼就會變成這樣:

- (void)viewContainerDidLoad

{

[self.viewContainer addSubview:self.label];

[self.viewContainer addSubview:self.tableView];

[self.viewContainer addSubview:self.button];

[self.viewContainer addSubview:self.textField];

}

... ...

僅僅改了個名字,現(xiàn)在是不是感覺清晰了很多筝野?如果再要說詳細(xì)一點(diǎn)晌姚,我們平常所認(rèn)為的服務(wù)端MVC是這樣劃分的:

但事實(shí)上,整套流程的MVC劃分是這樣:

由圖中可以看出歇竟,我們服務(wù)端開發(fā)在這個概念下挥唠,其實(shí)只涉及M和C的開發(fā)工作,瀏覽器作為View的容器焕议,負(fù)責(zé)View的展示和事件的監(jiān)聽宝磨。那么對應(yīng)到iOS客戶端的MVC劃分上面來,就是這樣:

唯一區(qū)別在于盅安,View的容器在服務(wù)端唤锉,是由Browser負(fù)責(zé),在整個網(wǎng)站的流程中别瞭,這個容器放在Browser是非常合理的窿祥。在iOS客戶端,View的容器是由UIViewController中的view負(fù)責(zé)蝙寨,我也覺得蘋果做的這個選擇是非常正確明智的晒衩。

因?yàn)闉g覽器和服務(wù)端之間的關(guān)系非常松散嗤瞎,而且他們分屬于兩個不同陣營,服務(wù)端將對View的描述生成之后听系,交給瀏覽器去負(fù)責(zé)展示贝奇,然而一旦view上有什么事件產(chǎn)生,基本上是很少傳遞到服務(wù)器(也就是所謂的Controller)的(要傳也可以:AJAX)靠胜,都是在瀏覽器這邊把事情都做掉弃秆,所以在這種情況下,View容器就適合放在瀏覽器(V)這邊髓帽。

但是在iOS開發(fā)領(lǐng)域窄锅,雖然也有讓View去監(jiān)聽事件的做法,但這種做法非常少僵腺,都是把事件回傳給Controller呵晚,然后Controller再另行調(diào)度。所以這時候必盖,View的容器放在Controller就非常合適拌牲。Controller可以因?yàn)椴煌录漠a(chǎn)生去很方便地更改容器內(nèi)容,比如加載失敗時歌粥,把容器內(nèi)容換成失敗頁面的View塌忽,無網(wǎng)絡(luò)時,把容器頁面換成無網(wǎng)絡(luò)的View等等失驶。

在iOS開發(fā)領(lǐng)域中土居,怎樣才算是MVC劃分的正確姿勢?

這個問題其實(shí)在上面已經(jīng)解答掉一部分了嬉探,那么這個問題的答案就當(dāng)是對上面問題的一個總結(jié)吧擦耀。

M應(yīng)該做的事:

給ViewController提供數(shù)據(jù)

給ViewController存儲數(shù)據(jù)提供接口

提供經(jīng)過抽象的業(yè)務(wù)基本組件,供Controller調(diào)度

C應(yīng)該做的事:

管理View Container的生命周期

負(fù)責(zé)生成所有的View實(shí)例涩堤,并放入View Container

監(jiān)聽來自View與業(yè)務(wù)有關(guān)的事件眷蜓,通過與Model的合作,來完成對應(yīng)事件的業(yè)務(wù)胎围。

V應(yīng)該做的事:

響應(yīng)與業(yè)務(wù)無關(guān)的事件吁系,并因此引發(fā)動畫效果,點(diǎn)擊反饋(如果合適的話白魂,盡量還是放在View去做)等汽纤。

界面元素表達(dá)

我通過與服務(wù)端MVC劃分的對比來回答了這兩個問題,之所以這么做碧聪,是因?yàn)槲抑烙泻芏鄆OS工程師之前是從服務(wù)端轉(zhuǎn)過來的冒版。我也是這樣,在進(jìn)安居客之前逞姿,我也是做服務(wù)端開發(fā)的辞嗡,在學(xué)習(xí)iOS的過程中捆等,我也曾經(jīng)對iOS領(lǐng)域的MVC劃分問題產(chǎn)生過疑惑,我疑惑的點(diǎn)就是前面開篇我猜測的點(diǎn)续室。如果有人問我iOS中應(yīng)該怎么做MVC的劃分栋烤,我就會像上面這么回答。

MVCS

蘋果自身就采用的是這種架構(gòu)思路挺狰,從名字也能看出明郭,也是基于MVC衍生出來的一套架構(gòu)。從概念上來說丰泊,它拆分的部分是Model部分薯定,拆出來一個Store。這個Store專門負(fù)責(zé)數(shù)據(jù)存取瞳购。但從實(shí)際操作的角度上講话侄,它拆開的是Controller。

這算是瘦Model的一種方案学赛,瘦Model只是專門用于表達(dá)數(shù)據(jù)年堆,然后存儲、數(shù)據(jù)處理都交給外面的來做盏浇。MVCS使用的前提是变丧,它假設(shè)了你是瘦Model,同時數(shù)據(jù)的存儲和處理都在Controller去做绢掰。所以對應(yīng)到MVCS痒蓬,它在一開始就是拆分的Controller。因?yàn)镃ontroller做了數(shù)據(jù)存儲的事情曼月,就會變得非常龐大谊却,那么就把Controller專門負(fù)責(zé)存取數(shù)據(jù)的那部分抽離出來,交給另一個對象去做哑芹,這個對象就是Store。這么調(diào)整之后捕透,整個結(jié)構(gòu)也就變成了真正意義上的MVCS聪姿。

關(guān)于胖Model和瘦Model

我在面試和跟別人聊天時,發(fā)現(xiàn)知道胖Model和瘦Model的概念的人不是很多乙嘀。大約兩三年前國外業(yè)界曾經(jīng)對此有過非常激烈的討論末购,主題就是Fat model, skinny controller。現(xiàn)在關(guān)于這方面的討論已經(jīng)不多了虎谢,然而直到今天胖Model和瘦Model哪個更好盟榴,業(yè)界也還沒有定論,所以這算是目前業(yè)界懸而未解的一個爭議婴噩。我很少看到國內(nèi)有討論這個的資料擎场,所以在這里我打算補(bǔ)充一下什么叫胖Model什么叫瘦Model羽德。以及他們的爭論來源于何處。

什么叫胖Model迅办?

胖Model包含了部分弱業(yè)務(wù)邏輯宅静。胖Model要達(dá)到的目的是,Controller從胖Model這里拿到數(shù)據(jù)之后站欺,不用額外做操作或者只要做非常少的操作姨夹,就能夠?qū)?shù)據(jù)直接應(yīng)用在View上。舉個例子:

Raw Data:

timestamp:1234567

FatModel:

@property (nonatomic, assign) CGFloat timestamp;

- (NSString *)ymdDateString; // 2015-04-20 15:16

- (NSString *)gapString; // 3分鐘前矾策、1小時前磷账、一天前、2015-3-13 12:34

Controller:

self.dateLabel.text = [FatModel ymdDateString];

self.gapLabel.text = [FatModel gapString];

把timestamp轉(zhuǎn)換成具體業(yè)務(wù)上所需要的字符串贾虽,這屬于業(yè)務(wù)代碼逃糟,算是弱業(yè)務(wù)。FatModel做了這些弱業(yè)務(wù)之后榄鉴,Controller就能變得非常skinny履磨,Controller只需要關(guān)注強(qiáng)業(yè)務(wù)代碼就行了。眾所周知庆尘,強(qiáng)業(yè)務(wù)變動的可能性要比弱業(yè)務(wù)大得多剃诅,弱業(yè)務(wù)相對穩(wěn)定,所以弱業(yè)務(wù)塞進(jìn)Model里面是沒問題的驶忌。另一方面矛辕,弱業(yè)務(wù)重復(fù)出現(xiàn)的頻率要大于強(qiáng)業(yè)務(wù),對復(fù)用性的要求更高付魔,如果這部分業(yè)務(wù)寫在Controller聊品,類似的代碼會灑得到處都是,一旦弱業(yè)務(wù)有修改(弱業(yè)務(wù)修改頻率低不代表就沒有修改)几苍,這個事情就是一個災(zāi)難翻屈。如果塞到Model里面去,改一處很多地方就能跟著改妻坝,就能避免這場災(zāi)難伸眶。

然而其缺點(diǎn)就在于,胖Model相對比較難移植刽宪,雖然只是包含弱業(yè)務(wù)厘贼,但好歹也是業(yè)務(wù),遷移的時候很容易拔出蘿卜帶出泥圣拄。另外一點(diǎn)嘴秸,MVC的架構(gòu)思想更加傾向于Model是一個Layer,而不是一個Object,不應(yīng)該把一個Layer應(yīng)該做的事情交給一個Object去做岳掐。最后一點(diǎn)凭疮,軟件是會成長的,F(xiàn)atModel很有可能隨著軟件的成長越來越Fat岩四,最終難以維護(hù)哭尝。

什么叫瘦Model?

瘦Model只負(fù)責(zé)業(yè)務(wù)數(shù)據(jù)的表達(dá)剖煌,所有業(yè)務(wù)無論強(qiáng)弱一律扔到Controller材鹦。瘦Model要達(dá)到的目的是,盡一切可能去編寫細(xì)粒度Model耕姊,然后配套各種helper類或方法來對弱業(yè)務(wù)做抽象桶唐,強(qiáng)業(yè)務(wù)依舊交給Controller。舉個例子:

Raw Data:

{

"name":"casa",

"sex":"male",

}

SlimModel:

@property (nonatomic, strong) NSString *name;

@property (nonatomic, strong) NSString *sex;

Helper:

#define Male 1;

#define Female 0;

+ (BOOL)sexWithString:(NSString *)sex;

Controller:

if ([Helper sexWithString:SlimModel.sex] == Male) {

...

}

由于SlimModel跟業(yè)務(wù)完全無關(guān)茉兰,它的數(shù)據(jù)可以交給任何一個能處理它數(shù)據(jù)的Helper或其他的對象尤泽,來完成業(yè)務(wù)。在代碼遷移的時候獨(dú)立性很強(qiáng)规脸,很少會出現(xiàn)拔出蘿卜帶出泥的情況坯约。另外,由于SlimModel只是數(shù)據(jù)表達(dá)莫鸭,對它進(jìn)行維護(hù)基本上是0成本闹丐,軟件膨脹得再厲害,SlimModel也不會大到哪兒去被因。

缺點(diǎn)就在于卿拴,Helper這種做法也不見得很好,這里有一篇文章批判了這個事情梨与。另外堕花,由于Model的操作會出現(xiàn)在各種地方,SlimModel在一定程度上違背了DRY(Don't Repeat Yourself)的思路粥鞋,Controller仍然不可避免在一定程度上出現(xiàn)代碼膨脹缘挽。

我的態(tài)度?嗯呻粹,我會在本門心法這一節(jié)里面說到踏。

說回來,MVCS是基于瘦Model的一種架構(gòu)思路尚猿,把原本Model要做的很多事情中的其中一部分關(guān)于數(shù)據(jù)存儲的代碼抽象成了Store,在一定程度上降低了Controller的壓力楣富。

MVVM

MVVM去年在業(yè)界討論得非常多凿掂,無論國內(nèi)還是國外都討論得非常熱烈,尤其是在ReactiveCocoa這個庫成熟之后,ViewModel和View的信號機(jī)制在iOS下終于有了一個相對優(yōu)雅的實(shí)現(xiàn)庄萎。MVVM本質(zhì)上也是從MVC中派生出來的思想踪少,MVVM著重想要解決的問題是盡可能地減少Controller的任務(wù)。不管MVVM也好糠涛,MVCS也好援奢,他們的共識都是Controller會隨著軟件的成長,變很大很難維護(hù)很難測試忍捡。只不過兩種架構(gòu)思路的前提不同集漾,MVCS是認(rèn)為Controller做了一部分Model的事情,要把它拆出來變成Store砸脊,MVVM是認(rèn)為Controller做了太多數(shù)據(jù)加工的事情具篇,所以MVVM把數(shù)據(jù)加工的任務(wù)從Controller中解放了出來,使得Controller只需要專注于數(shù)據(jù)調(diào)配的工作凌埂,ViewModel則去負(fù)責(zé)數(shù)據(jù)加工并通過通知機(jī)制讓View響應(yīng)ViewModel的改變驱显。

MVVM是基于胖Model的架構(gòu)思路建立的,然后在胖Model中拆出兩部分:Model和ViewModel瞳抓。關(guān)于這個觀點(diǎn)我要做一個額外解釋:胖Model做的事情是先為Controller減負(fù)埃疫,然后由于Model變胖,再在此基礎(chǔ)上拆出ViewModel孩哑,跟業(yè)界普遍認(rèn)知的MVVM本質(zhì)上是為Controller減負(fù)這個說法并不矛盾栓霜,因?yàn)榕諱odel做的事情也是為Controller減負(fù)。

另外臭笆,我前面說MVVM把數(shù)據(jù)加工的任務(wù)從Controller中解放出來叙淌,跟MVVM拆分的是胖Model也不矛盾。要做到解放Controller愁铺,首先你得有個胖Model鹰霍,然后再把這個胖Model拆成Model和ViewModel。

那么MVVM究竟應(yīng)該如何實(shí)現(xiàn)茵乱?

這很有可能是大多數(shù)人糾結(jié)的問題茂洒,我打算憑我的個人經(jīng)驗(yàn)試圖在這里回答這個問題,歡迎交流瓶竭。

在iOS領(lǐng)域大部分MVVM架構(gòu)都會使用ReactiveCocoa督勺,但是使用ReactiveCocoa的iOS應(yīng)用就是基于MVVM架構(gòu)的嗎?那當(dāng)然不是斤贰,我覺得很多人都存在這個誤區(qū)智哀,我面試過的一些人提到了ReactiveCocoa也提到了MVVM,但他們對此的理解膚淺得讓我忍俊不禁荧恍。嗯瓷叫,在網(wǎng)絡(luò)層架構(gòu)我會舉出不使用ReactiveCocoa的例子屯吊,現(xiàn)在舉我感覺有點(diǎn)兒早。

MVVM的關(guān)鍵是要有View Model摹菠!而不是ReactiveCocoa

注:MVVM要有ViewModel盒卸,以及ReactiveCocoa帶來的信號通知效果,在ReactiveCocoa里就是RAC等相關(guān)宏來實(shí)現(xiàn)次氨。另外蔽介,使用ReactiveCocoa能夠比較優(yōu)雅地實(shí)現(xiàn)MVVM模式,就是因?yàn)橛蠷AC等相關(guān)宏的存在煮寡。就像它的名字一樣Reactive-響應(yīng)式虹蓄,這也是區(qū)分MVVM的VM和MVC的C和MVP的P的一個重要方面。

ViewModel做什么事情洲押?就是把RawData變成直接能被View使用的對象的一種Model武花。舉個例子:

Raw Data:

{

(

(123, 456),

(234, 567),

(345, 678)

)

}

這里的RawData我們假設(shè)是經(jīng)緯度,數(shù)字我隨便寫的不要太在意杈帐。然后你有一個模塊是地圖模塊体箕,把經(jīng)緯度數(shù)組全部都轉(zhuǎn)變成MKAnnotation或其派生類對于Controller來說是弱業(yè)務(wù),(記住挑童,胖Model就是用來做弱業(yè)務(wù)的)累铅,因此我們用ViewModel直接把它轉(zhuǎn)變成MKAnnotation的NSArray,交給Controller之后Controller直接就可以用了站叼。

嗯娃兽,這就是ViewModel要做的事情,是不是覺得很簡單尽楔,看不出優(yōu)越性投储?

安居客Pad應(yīng)用也有一個地圖模塊,在這里我設(shè)計了一個對象叫做reformer(其實(shí)就是ViewModel)阔馋,專門用來干這個事情玛荞。那么這么做的優(yōu)越性體現(xiàn)在哪兒呢?

安居客分三大業(yè)務(wù):租房呕寝、二手房勋眯、新房。這三個業(yè)務(wù)對應(yīng)移動開發(fā)團(tuán)隊(duì)有三個API開發(fā)團(tuán)隊(duì)下梢,他們各自為政客蹋,這就造成了一個結(jié)果:三個API團(tuán)隊(duì)回饋給移動客戶端的數(shù)據(jù)內(nèi)容雖然一致,但是數(shù)據(jù)格式是不一致的孽江,也就是相同value對應(yīng)的key是不一致的讶坯。但展示地圖的ViewController不可能寫三個,所以肯定少不了要有一個API數(shù)據(jù)兼容的邏輯岗屏,這個邏輯我就放在reformer里面去做了闽巩,于是業(yè)務(wù)流程就變成了這樣:

這么一來钧舌,原本復(fù)雜的MKAnnotation組裝邏輯就從Controller里面拆分了出來,Controller可以直接拿著Reformer返回的數(shù)據(jù)進(jìn)行展示涎跨。APIManager就屬于Model,reformer就屬于ViewModel崭歧。具體關(guān)于reformer的東西我會放在網(wǎng)絡(luò)層架構(gòu)來詳細(xì)解釋隅很。Reformer此時扮演的ViewModel角色能夠很好地給Controller減負(fù),同時率碾,維護(hù)成本也大大降低叔营,經(jīng)過reformer產(chǎn)出的永遠(yuǎn)都是MKAnnotation,Controller可以直接拿來使用所宰。

然后另外一點(diǎn)绒尊,還有一個業(yè)務(wù)需求是取附近的房源,地圖API請求是能夠hold住這個需求的仔粥,那么其他地方都不用變婴谱,在fetchDataWithReformer的時候換一個reformer就可以了,其他的事情都交給reformer躯泰。

那么ReactiveCocoa應(yīng)該扮演什么角色谭羔?

不用ReactiveCocoa也能MVVM,用ReactiveCocoa能更好地體現(xiàn)MVVM的精髓麦向。前面我舉到的例子只是數(shù)據(jù)從API到View的方向瘟裸,View的操作也會產(chǎn)生"數(shù)據(jù)",只不過這里的"數(shù)據(jù)"更多的是體現(xiàn)在表達(dá)用戶的操作上诵竭,比如輸入了什么內(nèi)容话告,那么數(shù)據(jù)就是text、選擇了哪個cell卵慰,那么數(shù)據(jù)就是indexPath沙郭。那么在數(shù)據(jù)從view走向API或者Controller的方向上,就是ReactiveCocoa發(fā)揮的地方呵燕。

我們知道棠绘,ViewModel本質(zhì)上算是Model層(因?yàn)槭桥諱odel里面分出來的一部分),所以View并不適合直接持有ViewModel再扭,那么View一旦產(chǎn)生數(shù)據(jù)了怎么辦氧苍?扔信號扔給ViewModel,用誰扔泛范?ReactiveCocoa让虐。

在MVVM中使用ReactiveCocoa的第一個目的就是如上所說,View并不適合直接持有ViewModel罢荡。第二個目的就在于赡突,ViewModel有可能并不是只服務(wù)于特定的一個View对扶,使用更加松散的綁定關(guān)系能夠降低ViewModel和View之間的耦合度。

那么在MVVM中惭缰,Controller扮演什么角色浪南?

大部分國內(nèi)外資料闡述MVVM的時候都是這樣排布的:View <-> ViewModel <-> Model,造成了MVVM不需要Controller的錯覺漱受,現(xiàn)在似乎發(fā)展成業(yè)界開始出現(xiàn)MVVM是不需要Controller的络凿。的聲音了。其實(shí)MVVM是一定需要Controller的參與的昂羡,雖然MVVM在一定程度上弱化了Controller的存在感絮记,并且給Controller做了減負(fù)瘦身(這也是MVVM的主要目的)。但是虐先,這并不代表MVVM中不需要Controller怨愤,MMVC和MVVM他們之間的關(guān)系應(yīng)該是這樣:

(來源:http://www.sprynthesis.com/2014/12/06/reactivecocoa-mvvm-introduction/)

View <-> C <-> ViewModel <-> Model,所以使用MVVM之后蛹批,就不需要Controller的說法是不正確的撰洗。嚴(yán)格來說MVVM其實(shí)是MVCVM。從圖中可以得知般眉,Controller夾在View和ViewModel之間做的其中一個主要事情就是將View和ViewModel進(jìn)行綁定了赵。在邏輯上,Controller知道應(yīng)當(dāng)展示哪個View甸赃,Controller也知道應(yīng)當(dāng)使用哪個ViewModel柿汛,然而View和ViewModel它們之間是互相不知道的,所以Controller就負(fù)責(zé)控制他們的綁定關(guān)系埠对,所以叫Controller/控制器就是這個原因络断。

前面扯了那么多,其實(shí)歸根結(jié)底就是一句話:在MVC的基礎(chǔ)上项玛,把C拆出一個ViewModel專門負(fù)責(zé)數(shù)據(jù)處理的事情貌笨,就是MVVM。然后襟沮,為了讓View和ViewModel之間能夠有比較松散的綁定關(guān)系锥惋,于是我們使用ReactiveCocoa,因?yàn)樘O果本身并沒有提供一個比較適合這種情況的綁定方法开伏。iOS領(lǐng)域里KVO膀跌,Notification,block固灵,delegate和target-action都可以用來做數(shù)據(jù)通信捅伤,從而來實(shí)現(xiàn)綁定,但都不如ReactiveCocoa提供的RACSignal來的優(yōu)雅巫玻,如果不用ReactiveCocoa丛忆,綁定關(guān)系可能就做不到那么松散那么好祠汇,但并不影響它還是MVVM。

在實(shí)際iOS應(yīng)用架構(gòu)中熄诡,MVVM應(yīng)該出現(xiàn)在了大部分創(chuàng)業(yè)公司或者老牌公司新App的iOS應(yīng)用架構(gòu)圖中可很,據(jù)我所知易寶支付旗下的某個iOS應(yīng)用就整體采用了MVVM架構(gòu),他們抽出了一個Action層來裝各種ViewModel粮彤,也是屬于相對合理的結(jié)構(gòu)根穷。

所以Controller在MVVM中,一方面負(fù)責(zé)View和ViewModel之間的綁定导坟,另一方面也負(fù)責(zé)常規(guī)的UI邏輯處理。

VIPER

VIPER(View圈澈,Interactor惫周,Presenter,Entity康栈,Routing)递递。VIPER我并沒有實(shí)際使用過,我是在objc.io上第13期看到的啥么。

但凡出現(xiàn)一個新架構(gòu)或者我之前并不熟悉的新架構(gòu)登舞,有一點(diǎn)我能夠非常肯定悬荣,這貨一定又是把MVC的哪個部分給拆開了(壞笑菠秒,做這種判斷的理論依據(jù)在第一篇文章里面我已經(jīng)講過了)。事實(shí)情況是VIPER確實(shí)拆了很多很多氯迂,除了View沒拆践叠,其它的都拆了。

我提到的這兩篇文章關(guān)于VIPER都講得很詳細(xì)嚼蚀,一看就懂禁灼。但具體在使用VIPER的時候會有什么坑或者會有哪些爭議我不是很清楚,硬要寫這一節(jié)的話我只能靠YY轿曙,所以我想想還是算了弄捕。如果各位讀者有誰在實(shí)際App中采用VIPER架構(gòu)的或者對VIPER很有興趣的,可以評論區(qū)里面提出來导帝,我們交流一下守谓。

編后語

為了更好地向讀者輸出更優(yōu)質(zhì)的內(nèi)容,InfoQ將精選來自國內(nèi)外的優(yōu)秀文章舟扎,經(jīng)過整理審校后分飞,發(fā)布到網(wǎng)站。本篇文章作者為田偉宇睹限,原文鏈接為Casa Taloyum譬猫。本文已由原作者授權(quán)InfoQ中文站轉(zhuǎn)載讯檐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市染服,隨后出現(xiàn)的幾起案子别洪,更是在濱河造成了極大的恐慌,老刑警劉巖柳刮,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挖垛,死亡現(xiàn)場離奇詭異,居然都是意外死亡秉颗,警方通過查閱死者的電腦和手機(jī)痢毒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚕甥,“玉大人哪替,你說我怎么就攤上這事」交常” “怎么了凭舶?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長爱沟。 經(jīng)常有香客問我帅霜,道長,這世上最難降的妖魔是什么呼伸? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任身冀,我火速辦了婚禮,結(jié)果婚禮上蜂大,老公的妹妹穿的比我還像新娘闽铐。我一直安慰自己,他們只是感情好奶浦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布兄墅。 她就那樣靜靜地躺著,像睡著了一般澳叉。 火紅的嫁衣襯著肌膚如雪隙咸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天成洗,我揣著相機(jī)與錄音五督,去河邊找鬼。 笑死瓶殃,一個胖子當(dāng)著我的面吹牛充包,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼基矮,長吁一口氣:“原來是場噩夢啊……” “哼淆储!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起家浇,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤本砰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后钢悲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體点额,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年莺琳,在試婚紗的時候發(fā)現(xiàn)自己被綠了还棱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡惭等,死狀恐怖诱贿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咕缎,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布料扰,位于F島的核電站凭豪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏晒杈。R本人自食惡果不足惜嫂伞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拯钻。 院中可真熱鬧帖努,春花似錦、人聲如沸粪般。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亩歹。三九已至匙监,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間小作,已是汗流浹背亭姥。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留顾稀,地道東北人达罗。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像静秆,于是被迫代替她去往敵國和親粮揉。 傳聞我的和親對象是個殘疾皇子巡李,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容