前言git
前一段時間一直在學習iOS的架構。爲何呢?github
公司的架構一直是MVC,當咱們正式上線的時候,項目已經有了超十萬行代碼。主要的VC通常都有2000行代碼以上。編程
關鍵是,目前版本咱們只作了三分之一的業務邏輯…api
因此,架構重構吧。網絡
正文架構
MVVM框架
MVVM: Model-View-ViewModeldom
MVVM實際上是MVC的進化版,它將業務邏輯從VC中解耦到ViewModel,來實現VC大’瘦身’。分佈式
用代碼解釋吧!ide
作一個簡單的登陸判斷:
建立LoginViewModel(邏輯處理),LoginModel(只放數據),LoginViewController。
這裏不用LoginView是爲了讓初學者能更好的把精力集中在用ViewModel解耦上。
固然要是你這些都明白,你能夠直接看Wzxhaha/RandomerFramework,這是我在作的獨立項目Randomer的基本架構(SubClasses+Protocol+MVVM+RAC)以及它的登陸註冊模塊。另外,感謝王隆帥的這篇文章爲我打開了新世界的大門。
在LoginModel中加入方法
//.h
- (instancetype)initWithUserName:(NSString *)username password:(NSString *)password;
@property (nonatomic,copy,readonly)NSString * username;
@property (nonatomic,copy,readonly)NSString * password;
//.m
- (instancetype)initWithUserName:(NSString *)username password:(NSString *)password {
if (self = [super init]) {
_username = username;
_password = password;
}
return self;
}
這個沒什麼好講的,就是給Model加一個初始化方法。
在LoginViewModel中加入方法
#import "PersonModel.h"
- (instancetype)initWithPerson:(PersonModel *)person;
@property (nonatomic,assign,readonly)BOOL canLogin;
- (instancetype)initWithPerson:(PersonModel *)person {
if (self = [super init]) {
//在這作你綁定model後的處理
_canLogin = [self valiCanLoginWithUserName:person.username password:person.password];
}
return self;
}
- (BOOL)valiCanLoginWithUserName:(NSString *)username password:(NSString *)password {
if (username.length & password.length) {
return YES;
} else {
return NO;
}
}
給ViewModel添加個綁定Model的初始化方法,以及判斷賬號密碼是否有效的方法。
而後VC(或者View)就能夠直接這樣得到判斷後的結果
PersonModel * person = [[PersonModel alloc]initWithUserName:@"10" password:@"10"];
PersonViewModel * viewModel = [[PersonViewModel alloc]initWithPerson:person];
NSLog(@"%d",viewModel.canLogin);
簡單的功能的時候沒什麼,當你處理複雜的邏輯判斷的時候,MVVM會有巨大優點。
順便講一下ReactiveCocoa,我之因此這麼推崇MVVM,主要就是由於RAC和MVVM簡直太配了!
ReactiveCocoa
RAC具備函數式編程和響應式編程的特性,要是對編程思想不熟的能夠看個人WZXProgrammingIdeas
RAC最大的用處就是能監聽到各個事件,RAC把這個叫作信號流,而後接受信號經過block回調,裏面大量的使用了block,因此必定要用好@weakify(self)和@strongify(self)。
爲何說RAC和MVVM太配了?
MVVM是把方法解耦到ViewModel,可是仍是要VC(V)調用的,那麼判斷何時調用的邏輯仍是會複雜。
而RAC解決了這個問題,它負責監聽事件,而後調用ViewModel來進行邏輯判斷。
例如:
[[_registerBtn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
@strongify(self)
[self.viewModel toRegisterWithType:Register];
}];
[[_loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
@strongify(self)
[self.viewModel loginWithUserName:self.usernameTextField.text password:self.usernameTextField.text Success:^(id response) {
} failure:^{
SHOW_ERROR(@"錯誤", @"帳號或密碼錯誤")
} error:^(NSError *error) {
SHOW_ERROR(@"錯誤", @"網絡鏈接失敗")
}];
}];
RAC監聽了登陸和註冊按鈕,使得代碼簡潔,並且結構十分緊湊。
Demo的話仍是看這個吧Wzxhaha/RandomerFramework
https://github.com/Wzxhaha/RandomerFramework
或者簡單版的WZXRACDemo
https://github.com/Wzxhaha/WZXRACDemo
鏈式網絡請求框架
爲何封裝WZXNetworking
這是一個容錯性很是嚇人的框架。
[[WZXNetworkManager manager].setRequest(@"http://192.168.1.40:8001").RequestType(POST).HTTPHeader(nil).Parameters(nil).RequestSerialize(RequestSerializerHTTP).ResponseSerialize(ResponseSerializerJSON) startRequestWithSuccess:^(id response) {
NSLog(@"success");
} failure:^{
NSLog(@"failure");
}];
在這裏除了.setRequest(url)和startRequestWithSuccess failure方法,其餘都是非必要的。
你能夠這樣:
[[WZXNetworkManager manager].setRequest(@"http://192.168.1.40:8001") startRequestWithSuccess:^(id response) {
NSLog(@"success");
} failure:^{
NSLog(@"failure");
}];
鏈式在參數和參數的選擇不少的狀況或者頗有可能改動的狀況下展示了驚人的優點。由於,它的改動十分方便,只不過添加或者修改一個方法。
打個比方:
換成集中式API封裝應該是這樣的:
- (void)GET:(NSString *)url
parameters:(id)Parameters
success:(SuccessBlock)success
failure:(FailureBlock)failure;
當你要添加一個Version屬性作API版本判斷的時候,你能怎麼辦?只能重寫方法,在方法中加入一個Version參數,而後全部使用的網絡請求都要改變方法。
換成分佈式API封裝咱們則不考慮對比了..
GeneralAPI *apiGeGet = [[GeneralAPI alloc] initWithRequestMethod:@"get"];
apiGeGet.apiRequestMethodType = RequestMethodTypeGET;
apiGeGet.apiRequestSerializerType = RequestSerializerTypeHTTP;
apiGeGet.apiResponseSerializerType = ResponseSerializerTypeHTTP;
[apiGeGet setApiCompletionHandler:^(id responseObject, NSError * error) {
NSLog(@"responseObject is %@", responseObject);
if (error) {
NSLog(@"Error is %@", error.localizedDescription);
}
}];
[apiGeGet start];
這樣的結構是否太鬆散?
再換成WZXNetworking
咱們要作的只是再添加一個方法和一個成員變量,而後在原有方法後面加一個.method()
- (WZXNetworkManager * (^) (id some))method {
return ^WZXNetworkManager (id some) {
self.XXX = some
return self;
}
}
[[WZXNetworkManager manager].setRequest(@"http://192.168.1.40:8001").method(some) startRequestWithSuccess:^(id response) {
NSLog(@"success");
} failure:^{
NSLog(@"failure");
}];
代碼放這:WZXNetworking
https://github.com/Wzxhaha/WZXNetworking
至於鏈式是怎麼實現的,仍是看那個WZXProgrammingIdeas
https://github.com/Wzxhaha/WZXProgrammingIdeas