ViewController規範化寫法

一、ViewController代碼佈局

在一個規範的開發團隊中,在代碼規範上應該達到每個程序員所寫出來的ViewController結構一致的目標,這樣的規範能減小各類delegate getter隨機出現,ViewController lifecycle方法處處都是。制定一個規範,可使代碼更有利於閱讀和維護,固然,規範的制定和團隊的架構師的經驗而定。
OS應用架構談 view層的組織和調用方案這篇文章裏提供一個佈局規範,以下圖所示:
clipboard.pnghtml

此外,全部須要初始化的屬性能夠放在getter方法中。ios

#pragma mark - life cycle
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:self.firstTableView];
    [self.view addSubview:self.secondTableView];
    [self.view addSubview:self.firstFilterLabel];
    [self.view addSubview:self.secondFilterLabel];
    [self.view addSubview:self.cleanButton];
    [self.view addSubview:self.originImageView];
    [self.view addSubview:self.processedImageView];
    [self.view addSubview:self.activityIndicator];
    [self.view addSubview:self.takeImageButton];
}

避免出現如下這種狀況程序員

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.textLabel = [[UILabel alloc] init];
    self.textLabel.textColor = [UIColor blackColor];
    self.textLabel ... ...
    self.textLabel ... ...
    self.textLabel ... ...
    [self.view addSubview:self.textLabel];
}

通常狀況下,View的建立是應該放在View中處理。這兩種初始化方式相比,很明顯第一種比較簡潔,將初始化放到getter中,分工明確,也有利於作測試。數據庫

二、輕量化ViewController

在ios的開發中,MVC的核心就在ViewController,有些開發人員在不熟悉MVC的狀況下,把全部東西通通放入C裏面就能夠了,因此每每一個ViewController既有View的建立,也會有Model裏的業務邏輯。這樣一個臃腫的ViewController很差閱讀,很差維護,更不利於作單元測試。
Lighter View Controllers文章裏提出了幾個應該屬於ViewController規範化問題的概念。數組

2.一、將DataSource和其餘Protocols隔離

精簡 ViewController 的有效方法之一就是實現 UITableViewDataSource 協議相關的代碼封裝成一個類(好比本文中的 ArraryDataSource )。若是你常常在 UIViewController 中實現 UITableViewDataSource 協議,你會發現相關代碼看起來都差很少。緩存

舉例說明:網絡

# pragma mark Pragma 

- (Photo*)photoAtIndexPath:(NSIndexPath*)indexPath 
{
    return photos[(NSUInteger)indexPath.row];
}

- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
    return photos.count;
}

- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
    PhotoCell* cell = [tableView dequeueReusableCellWithIdentifier:PhotoCellIdentifier forIndexPath:indexPath];
    Photo* photo = [self photoAtIndexPath:indexPath];
    cell.label.text = photo.name;
    return cell;
}

能夠看到上面方法的實現都與 NSArray 有關,還有一個方法的實現與 Photo 有關(Photo 與 Cell 呈一一對應關係)。下面讓咱們來把與 NSArray 相關的代碼從 ViewController 中抽離出來,並改用 block 來設置 cell 的視圖。架構

@implementation ArrayDataSource

- (id)itemAtIndexPath:(NSIndexPath*)indexPath 
{
    return items[(NSUInteger)indexPath.row];
}

- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section 
{
    return items.count;
}

- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath 
{
    id cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
    id item = [self itemAtIndexPath:indexPath];
   
    configureCellBlock(cell,item);
   
    return cell;
}

@end

如今你能夠把 ViewController 中的相關方法移除,而且把 ViewController 的 dataSource 設置爲 ArrayDataSource 的實例。佈局

void (^configureCell)(PhotoCell*, Photo*) = ^(PhotoCell* cell, Photo* photo) {
cell.label.text = photo.name;
};

photosArrayDataSource = [[ArrayDataSource alloc] initWithItems:photos
cellIdentifier:PhotoCellIdentifier
configureCellBlock:configureCell];
self.tableView.dataSource = photosArrayDataSource;

經過上面的方法,你就能夠把設置 Cell 視圖的工做從 ViewController 中抽離出來。如今你不須要再關心indexPath如何與 NSArrary 中的元素如何關聯,當你須要將數組中的元素在其它 UITableView 中展現時你能夠重用以上代碼。你也能夠在 ArrayDataSource 中實現更多的方法,好比tableView:commitEditingStyle:forRowAtIndexPath:單元測試

除了以上好處以外,咱們還能夠針對這部分實現編寫單獨的單元測試,並且不再須要處處複製粘貼了。當你使用其餘數據容器時,你能夠用相似的方式來達到代碼複用的效果。

該技巧一樣適用於其餘 Protocol ,好比 UICollectionViewDataSource 。經過該協議,你能夠定義出各類各樣的 UICollectionViewCell 。假若有一天,你須要在代碼在使用到 UICollectionView 來替代當前的 UITableView,你只須要修改幾行 ViewController 中的代碼便可完成替換。你甚至可以讓你的 DataSource 類同時實現 UICollectionViewDataSource 協議和 UITableViewDataSource 協議。

除此以外,還有不少關於這個方面的討論:
UITableview代理方法與Viewcontroller分離
不要把 ViewController 變成處理 tableView 的"垃圾桶"

2.二、將業務邏輯移至Model層

下面的示例代碼(另一個工程)位於view controller,做用是找出針對用戶active priority的一個列表。

- (void)loadPriorities { 
NSDate* now = [NSDate date]; 
NSString* formatString = @"startDate <= %@ AND endDate >= %@"; 
NSPredicate* predicate = [NSPredicate predicateWithFormat:formatString, now, now]; 
NSSet* priorities = [self.user.priorities filteredSetUsingPredicate:predicate]; 
self.priorities = [priorities allObjects]; 
}

實際上,若是把這個方法移至User類的一個category中,會讓代碼更加清晰。此時,在View Controller.m文件中看起來應該是這樣的:

- (void)loadPriorities 
{ 
    self.priorities = [user currentPriorities]; 
}

而在User+Extensions.m中則以下代碼:

  • (NSArray*)currentPriorities {
    NSDate* now = [NSDate date];

NSString* formatString = @"startDate <= %@ AND endDate >= %@";
NSPredicate* predicate = [NSPredicate predicateWithFormat:formatString, now, now];
return [[self.priorities filteredSetUsingPredicate:predicate] allObjects];
}

實際開發中,有一些代碼很難將其移至model對象中,可是,很明顯這些代碼與model是相關的,針對這樣的狀況,咱們能夠單獨爲其寫一個類,例以下面的store類。

建立Store類

本文給出示例工程的初版代碼中,有一部分代碼是用來從文件中加載數據,並對其進行解析的,這些代碼是在view controller中:

- (void)readArchive { 
NSBundle* bundle = [NSBundle bundleForClass:[self class]]; 
NSURL *archiveURL = [bundle URLForResource:@"photodata" 
withExtension:@"bin"]; 
NSAssert(archiveURL != nil, @"Unable to find archive in bundle."); 
NSData *data = [NSData dataWithContentsOfURL:archiveURL 
options:0 
error:NULL]; 
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; 
_users = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"users"]; 
_photos = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"photos"]; 
[unarchiver finishDecoding]; 
}

實際上,view controller不該該關心這些事情的。在示例工程中,我建立了一個Store類來作這些事情——經過將這些代碼從view controller中剝離出來,不只能夠對其重用和單獨測試,另外還能對view controller瘦身。Store類專一於數據的加載、緩存,以及對數據庫進行配置。這裏的Store也常常叫作service layer或者repository。

2.三、其餘輕量化規範

  1. 將網絡請求相關邏輯放到Model層處理

  2. View的建立放到View層處理

三、採用比較清晰的架構

如何給UIViewController瘦身這篇文章很是詳細的介紹了臃腫的VC會致使什麼問題的出現,同時也提出一個很是清晰的架構圖,以下:

clipboard.png

四、總結

只有開發人員在徹底遵照架構規範進行開發時,架構規範的優點才能徹底體現出來。評判一個好的規範的標準很簡單,按照規範寫出來的代碼是否易於閱讀、維護、擴展、測試。每每一個成熟的代碼規範不是一蹴而就的,須要在實際的開發過程當中,不斷的去修改完善。可是最重要的一點,適合本身的纔是最好的!

相關文章
相關標籤/搜索