工作到現(xiàn)在,對代碼稍微有點認(rèn)識,if else 會寫那么點。今天就和大家談?wù)勊斫獾腗VC,以及現(xiàn)在比較流行的MVVM。首先我們應(yīng)該明白,計算機實現(xiàn)一個功能核心代碼就那么點。也許我們經(jīng)常會聽到對于同一個問題,菜鳥的實現(xiàn)的真的就是幾行,可是大牛卻多出了很多文件。這個是為什么?因為大牛的“經(jīng)驗”比較多。。。這里面最主要的目的就是為了維護和可擴展。在設(shè)計模式里面,如果你能遵循單一原則,你的代碼就已經(jīng)很好了。
MVC
做iOS開發(fā),一直被教導(dǎo)一定要按MVC模式開發(fā)??墒荕VC到底是神馬?至少在我工作初也不懂是咋回事!如果網(wǎng)上一搜就是千篇一律的M是數(shù)據(jù)模型,V是視圖,C是控制器。然后巴拉巴拉的講他們之間是怎么通信的。然并無卵用!iOS的MVC展現(xiàn)形式還是有點特殊的,特別是controller和view緊密在一起,controller還必須負(fù)責(zé)view的展示。在服務(wù)器端,view根據(jù)model直接生成HTML,然后直接扔給瀏覽器去渲染和展示,通過Ajax或者js post告訴服務(wù)器controller view的響應(yīng)事件,controller真的做的只是業(yè)務(wù)數(shù)據(jù)的處理,出來的結(jié)果其實還是數(shù)據(jù),根本沒有去做UI相關(guān)的事情。也可以說服務(wù)器根本就沒有view。。但它有個瀏覽器,幫他管理著view和controller之間的交互。而在我們iOS中,controller可不僅僅只是處理數(shù)據(jù)了,還負(fù)責(zé)view的管理以及事件的傳遞。 MVC本質(zhì)就是將數(shù)據(jù)展示和數(shù)據(jù)進行進行隔離,提高代碼的復(fù)用性和擴展性。好吧,我也說點并無什么卵用的。
看看斯坦福老爺爺?shù)囊粡垐D:
這就是我們所認(rèn)識的MVC。我們可以看到,Controller可以和Model通信,也可以和View進行通信。繼續(xù)看Controller和Model的關(guān)系,綠色的箭頭代表Controller可以直接進行對Model進行訪問,也就是說Model對于Controller來說就是透明的。但是Model并不知道Controller是誰。如果Model發(fā)生了變化,那么就通過Notification和KVO的方式傳遞給Controller。同樣的Controller和View之間也是這種關(guān)系,View對Controller來說就是 透明的。Controller可以直接根據(jù)Model決定View的展示。View如果接受響應(yīng)事件則通過delegate,target-action,block等方式告訴Controller的狀態(tài)變化。Controller進行業(yè)務(wù)的處理,然后再控制View的展示。
到這里你會發(fā)現(xiàn)Model和View并不能直接的進行通信,都必須通過Controller。那這樣Model和View就是相互獨立的。View只負(fù)責(zé)頁面的展示,Model只是數(shù)據(jù)的存儲,那么也就達(dá)到了解耦和重用的目的。
說這么多不如幾行代碼來的實在。我們以一個簡單的例子來看下:
我們假設(shè)蘋果根據(jù)買iPhone的人給予不同的優(yōu)惠,學(xué)生優(yōu)惠20%,it民工優(yōu)惠50%,其他不優(yōu)惠。
//客戶類
typedef NS_ENUM(NSInteger, CustomerType) {
CustomerTypeStudent,
CustomerTypeiT,
CustomerTypeOther,
};
@interface Customer : NSObject
@property (nonatomic, assign) CustomerType customerType;
@end
//iPhone類
@interface iPhone : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *price;
@end
//VC類
@interface ViewController ()
@property (nonatomic, strong) iPhone *iphone;
@property (nonatomic, strong) Customer *customer;
@property (weak, nonatomic) IBOutlet UILabel *lblName;
@property (weak, nonatomic) IBOutlet UILabel *lblPrice;
@property (weak, nonatomic) IBOutlet UILabel *lblDiscount;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"手機優(yōu)惠";
self.lblName.text = self.iphone.name;
self.lblPrice.text = self.iphone.price;
if (self.customer.customerType == CustomerTypeStudent) {
self.lblDiscount.text = @"優(yōu)惠20%";
}
else if (self.customer.customerType == CustomerTypeiT) {
self.lblDiscount.text = @"優(yōu)惠50%";
}
else {
self.lblDiscount.text = @"沒有優(yōu)惠";
}
}
@end
這個就是我們最正常的開發(fā),我們的邏輯都是在vc里面寫的。這樣寫有木有錯呢?一點沒錯,controller本來就是用來處理業(yè)務(wù)的。由于這個例子比較簡單,只是做了個優(yōu)惠判斷,所以我們看不出有啥壞處。有點開發(fā)經(jīng)驗的都知道,如果業(yè)務(wù)復(fù)雜起來,再加上其他亂七八糟的驗證,controller就會變得很大,越來越難以維護。這個也是MVC比較明顯的缺點。
MVVM
既然controller越來越臃腫,越來越難以維護,我們怎么去優(yōu)化和瘦身呢?回頭再仔細(xì)看看我們所謂的業(yè)務(wù)邏輯,是干什么的?無非就是根據(jù)幾個數(shù)據(jù)得出一個數(shù)據(jù)用來控制view的顯示。比如展示的是什么文案,按鈕能不能響應(yīng),頁面能不能跳轉(zhuǎn)等等。那MVVM就干了這件事,幫忙分擔(dān)一下controller里面的部分業(yè)務(wù)邏輯。MVVM更合理的應(yīng)該叫做MV-CM。
這個時候,controller將不再直接和真實的model進行綁定了,而通過ViewModel,viewModel進行持有真實的Model。
我們來看看剛剛那例子怎么修改:
//新建一個viewModel
//.h文件
@interface viewModel : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *price;
@property (nonatomic, strong) NSString *discount;
- (id)initWithCustomer:(Customer *)customer iphone:(iPhone *)iphone;
@end
//.m文件
@interface viewModel ()
@property (nonatomic, strong) iPhone *iphone;
@property (nonatomic, strong) Customer *customer;
@end
@implementation viewModel
- (id)initWithCustomer:(Customer *)customer iphone:(iPhone *)iphone
{
if (self = [super init]) {
_customer = customer;
_iphone = iphone;
[self bindData];
}
return self;
}
- (void)bindData
{
self.name = _iphone.name;
self.price = _iphone.price;
if (self.customer.customerType == CustomerTypeStudent) {
self.discount = @"優(yōu)惠20%";
}
else if (self.customer.customerType == CustomerTypeiT) {
self.discount = @"優(yōu)惠50%";
}
else {
self.discount = @"沒有優(yōu)惠";
}
}
@end
//VC
@interface ViewController ()
@property (nonatomic, strong) viewModel *viewModel;
@property (weak, nonatomic) IBOutlet UILabel *lblName;
@property (weak, nonatomic) IBOutlet UILabel *lblPrice;
@property (weak, nonatomic) IBOutlet UILabel *lblDiscount;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.title = @"手機優(yōu)惠";
self.lblName.text = self.viewModel.name;
self.lblPrice.text = self.viewModel.price;
self.lblDiscount.text = self.viewModel.discount;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
看到修改完的代碼,你會發(fā)現(xiàn)VC里面已經(jīng)省去了不少的代碼。一切都和viewModel進行交流。這里我只是展示一個最簡單的數(shù)據(jù)展示,如果有其他響應(yīng)事件,是需要viewModel開放方法來進行處理的,并要通知VC處理結(jié)果的。
關(guān)于MVVM的優(yōu)點:
-
方便測試
在MVC下,Controller基本是無法測試的,里面混雜了個各種邏輯,而且分散在不同的地方。有了MVVM我們就可以測試?yán)锩娴膙iewModel,來驗證我們的處理結(jié)果對不對(Xcode7的測試已經(jīng)越來越完善了)。
-
便于代碼的移植
比如iOS里面有iPhone版本和iPad版本,除了交互展示不一樣外,業(yè)務(wù)邏輯的model是一致的。這樣,我們就可以以很小的代價去開發(fā)另一個app。(以前做公司iPad的時候就深深感覺到,全部在VC里面是多么的痛苦和重新開發(fā)一個沒有啥區(qū)別)。
-
兼容MVC
MVVM是MVC的一個升級版,目前的MVC也可以很快的轉(zhuǎn)換到MVVM這個模式。VC可以省去一大部分展示邏輯。
缺點:
-
類會增多
每個VC都附帶一個viewModel,類的數(shù)量*2
-
viewModel會越來越龐大
我們把邏輯給了viewModel,那勢必Model也會變得很復(fù)雜,里面的屬性和方法越來越多??赡苤貙懙姆椒ū容^多,因為涉及到一些數(shù)據(jù)的轉(zhuǎn)換以及和controller之間的通信。
-
調(diào)用復(fù)雜度增加
由于數(shù)據(jù)都是從viewModel來,想想突然來了一個新人,一看代碼,不知道真實的模型是誰。比如常用tableview的數(shù)據(jù)源,一般都是一個數(shù)組,如果不斷的通過viewModel去取,溝通上沒有那么直接。況且每封一層,意味著要寫很多代碼去融合他們的轉(zhuǎn)換。
最后說下ReactiveCocoa這個框架,這個雖然和MVVM經(jīng)常一起出現(xiàn),這個框架主要是幫我們實現(xiàn)model和view的綁定機制。后面會有文章來介紹它。