iOS底層原理之架構設計
何為架構?
- 架構(Architecture):軟體開發中的設計方案,類與類之間的關係、模組與模組之間的關係、客戶端與服務端的關係。
- 經常聽到的架構名詞:MVC、MVP、MVVM、VIPER、CDD、三層架構、四層架構等。
MVC - Apple版
Model-View-Controller
是iOS開發中常用的模式 ,Model和View之間沒有任何直接到關係,通過Controller作為橋樑將二者聯絡起來。(Controller可初始化資料並將資料傳遞給Model;Model可以將資料傳遞給Controller,Controller將這些資料賦給View展示;View可以傳遞事件給Controller,Controller通過事件判斷區出來資料Model)。
優點
缺點
:Controller的程式碼過於臃腫。
MVC – 變種
Model-View-Controller
變種的MVC,每一個view對應一個Model,只要Controller設定View的Model,就可直接展示對應的示圖(常見的cell新增一個model屬性就是這種模式)。
優點
:對Controller進行瘦身,將View內部的細節封裝起來了,外界不知道View內部的具體實現;
缺點
:View依賴於Model。
MVP
- Model-View-Presenter
- MVP中的V在iOS中指的是ViewController和View。MVP將MVC的ViewController進行拆分:檢視資料邏輯處理部分為P,ViewController剩餘部分與View合併成V,V和P之間通過Protocol進行通訊。
- MVP實現了各模組的解藕,具有更好的可測試性。但是總體程式碼量比MVC大。
另外,iOS MVC更適用於快速開發,即程式碼規模較小的專案。因此將簡單的MVC的Demo改成MVP,反而會顯得笨拙。
#import "MJAppPresenter.h" #import "MJApp.h"//Model #import "MJAppView.h"//View @interface MJAppPresenter() <MJAppViewDelegate> @property (weak, nonatomic) UIViewController *controller; @end @implementation MJAppPresenter - (instancetype)initWithController:(UIViewController *)controller { if (self = [super init]) { self.controller = controller; // 建立View MJAppView *appView = [[MJAppView alloc] init]; appView.frame = CGRectMake(100, 100, 100, 150); appView.delegate = self; [controller.view addSubview:appView]; // 載入模型資料 MJApp *app = [[MJApp alloc] init]; app.name = @"QQ"; app.image = @"QQ"; // 賦值資料 [appView setName:app.name andImage:app.image]; } return self; } #pragma mark - MJAppViewDelegate - (void)appViewDidClick:(MJAppView *)appView { NSLog(@"presenter 監聽了 appView 的點選"); } @end #import "ViewController.h" #import "MJAppPresenter.h" @interface ViewController () @property (strong, nonatomic) MJAppPresenter *presenter; //@property (strong, nonatomic) MJOtherPresenter *presenter1; //@property (strong, nonatomic) MJNewsPresenter *presenter2; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.presenter = [[MJAppPresenter alloc] initWithController:self]; }
可以看出View和Model的都是直接和Presenter傳遞值和互動的,時間處理也在Presenter中,不同的View和Model可以給Controller新增不同的Presenter屬性即可,Controller不再直接是View和Model的橋樑。
MVVM
- Model-View-ViewModel
- MVVM 模式將 Presenter 改名為 ViewModel,基本上與 MVP 模式完全一致。 唯一的區別是,它採用雙向繫結(data-binding);當被繫結物件某個值的變化時,繫結物件會自動感知,無需被繫結物件主動通知繫結物件。可以使用KVO和RAC實現。例如在Label中顯示倒計時,是V綁定了包含定時器的VM。
- 雙向繫結在MVVM中指的是V和VM之間相互繫結。例如TextField的text長度達到閾值,另一個Button改變背景顏色。這個過程中首先VM感知V中TextField的text屬性長度變化,V感知VM中對應的狀態屬性。一旦V中TextField的text屬性長度超出VM中的閾值,VM中的狀態屬性改變,觸發V中Button的背景色發生改變。
- MVVM的優點:
- 低耦合。檢視(View)可以獨立於Model變化和修改,一個ViewModel可以繫結到不同的"View"上,當View變化的時候Model可以不變,當Model變化的時候View也可以不變。
- 可重用性。你可以把一些檢視邏輯放在一個ViewModel裡面,讓很多view重用這段檢視邏輯。
- 獨立開發。開發人員可以專注於業務邏輯和資料的開發(ViewModel),設計人員可以專注於頁面設計。
- 可測試。介面素來是比較難於測試的,而現在測試可以針對ViewModel來寫。
facebook
的KVOController
九可以用來雙向繫結,其github地址為:KVOController
//View
#import <UIKit/UIKit.h>
@class MJAppView, MJAppViewModel;
@protocol MJAppViewDelegate <NSObject>
@optional
- (void)appViewDidClick:(MJAppView *)appView;
@end
@interface MJAppView : UIView
@property (weak, nonatomic) MJAppViewModel *viewModel;
@property (weak, nonatomic) id<MJAppViewDelegate> delegate;
@end
#import "MJAppView.h"
#import "NSObject+FBKVOController.h"
@interface MJAppView()
@property (weak, nonatomic) UIImageView *iconView;
@property (weak, nonatomic) UILabel *nameLabel;
@end
@implementation MJAppView
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
UIImageView *iconView = [[UIImageView alloc] init];
iconView.frame = CGRectMake(0, 0, 100, 100);
[self addSubview:iconView];
_iconView = iconView;
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.frame = CGRectMake(0, 100, 100, 30);
nameLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:nameLabel];
_nameLabel = nameLabel;
}
return self;
}
- (void)setViewModel:(MJAppViewModel *)viewModel
{
_viewModel = viewModel;
__weak typeof(self) waekSelf = self;
//監聽viewModel的name和image屬性變化,一旦變化就給view賦值
[self.KVOController observe:viewModel keyPath:@"name" options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSKeyValueChangeKey,id> * _Nonnull change) {
waekSelf.nameLabel.text = change[NSKeyValueChangeNewKey];
}];
[self.KVOController observe:viewModel keyPath:@"image" options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSKeyValueChangeKey,id> * _Nonnull change) {
waekSelf.iconView.image = [UIImage imageNamed:change[NSKeyValueChangeNewKey]];
}];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if ([self.delegate respondsToSelector:@selector(appViewDidClick:)]) {
[self.delegate appViewDidClick:self];
}
}
@end
//ViewModel
#import <UIKit/UIKit.h>
@interface MJAppViewModel : NSObject
- (instancetype)initWithController:(UIViewController *)controller;
@end
#import "MJAppViewModel.h"
#import "MJApp.h"
#import "MJAppView.h"
@interface MJAppViewModel() <MJAppViewDelegate>
@property (weak, nonatomic) UIViewController *controller;
@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *image;
@end
@implementation MJAppViewModel
- (instancetype)initWithController:(UIViewController *)controller
{
if (self = [super init]) {
self.controller = controller;
// 建立View
MJAppView *appView = [[MJAppView alloc] init];
appView.frame = CGRectMake(100, 100, 100, 150);
appView.delegate = self;
appView.viewModel = self;//雙向繫結
[controller.view addSubview:appView];
// 載入模型資料
MJApp *app = [[MJApp alloc] init];
app.name = @"QQ";
app.image = @"QQ";
// 設定資料
self.name = app.name;
self.image = app.image;
}
return self;
}
#pragma mark - MJAppViewDelegate
- (void)appViewDidClick:(MJAppView *)appView
{
NSLog(@"viewModel 監聽了 appView 的點選");
}
@end
//Controller
#import "ViewController.h"
#import "MJAppViewModel.h"
@interface ViewController ()
@property (strong, nonatomic) MJAppViewModel *viewModel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.viewModel = [[MJAppViewModel alloc] initWithController:self];
}
@end
可以看出Controller 中只添加了ViewModel,不再與View和Model有任何關係,ViewModel初始化了View和Model,並將Model資料繫結給自己;而View綁定了ViewModel屬性,並監聽器值得變化,一旦有變化設定相應的控制元件;ViewModel為View代理,響應view的一些事件。
設計模式
- 設計模式(Design Pattern)
是一套被反覆使用、程式碼設計經驗的總結
使用設計模式的好處是:可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性
一般與程式語言無關,是一套比較成熟的程式設計思想 - 設計模式可以分為三大類
- 建立型模式:物件例項化的模式,用於解耦物件的例項化過程
單例模式、工廠方法模式,等等 - 結構型模式:把類或物件結合在一起形成一個更大的結構
代理模式、介面卡模式、組合模式、裝飾模式,等等 - 行為型模式:類或物件之間如何互動,及劃分責任和演算法
觀察者模式、命令模式、責任鏈模式,等等
面試題
-
講講 MVC、MVVM、MVP,以及你在專案裡具體是怎麼寫的?
把上面的講一下就好了。 -
你自己用過哪些設計模式?
單例、代理、觀察者模等等。 -
一般開始做一個專案,你的架構是如何思考的?
從實現方式思考,從結構模組思考,自己發揮。