ViewControllers 瘦身計劃 (一)

View controllers 一般是 iOS 項目中最大的文件,而且它們包含了許多沒必要要的代碼。因此 View controllers 中的代碼幾乎老是複用率最低的。接下來我將結合一些我本身看到的東西和平時在使用的方法,來節省ViewController中的代碼量。javascript

歡迎你們關注個人公衆號,我會按期分享一些我在項目中遇到問題的解決辦法和一些iOS實用的技巧,現階段主要是整理出一些基礎的知識記錄下來java



文章也會同步更新到個人博客:
ppsheep.com數組

把 Data Source 和其餘 Protocols 分離出來

咱們在平時的編碼中,最常用到的一個控件就是UITableView了,那咱們每次須要使用到tableview的時候,都須要寫一些重複的代碼,好比緩存

#pragma mark - tableview datasource

#pragma mark - tableview datasource

- (PPSFriend *)friendAtIndexPath:(NSIndexPath *)indexPath{
    return self.friends[indexPath.row];
}

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

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    PPSFriendCell *cell = [tableView dequeueReusableCellWithIdentifier:@"friendCell" forIndexPath:indexPath];
    PPSFriend *friend = [self friendAtIndexPath:indexPath];
    cell.textLabel.text = friend.name;
    return cell;
}複製代碼

像上面一些代碼,咱們天天都在寫,每次用到UITableView都須要重寫這些毫無技術可言的代碼,那咱們試想一下可否寫一個封裝類,將這些重複的方法所有封起來,屢次使用呢?答案固然是,能夠的。網絡

上面的代碼,其實都是在圍繞着friends這個數組作一系列的事情,咱們能夠獨立出來一個類,使用一個block或者delegate來設置cell,固然這取決於你的習慣。測試

#pragma mark - tableview config cell
- (void)configCell{
    void (^configCell)(PPSFriendCell *, PPSFriend *) = ^(PPSFriendCell *cell, PPSFriend *friend){
        cell.textLabel.text = friend.name;
    };
    PPSArrayDatasource *datasources = [[PPSArrayDatasource alloc] initWithItems:self.friends cellIdentifier:@"friendCell" configureCellBlock:configCell];
    self.tableView.dataSource = datasources;
}複製代碼

如今,你能夠把 view controller 中的這 3 個方法去掉了,取而代之,你能夠建立一個 PPSArrayDatasource 類的實例做爲 table view 的 data source。ui

如今你不用擔憂把一個 index path 映射到數組中的位置了,每次你想把這個數組顯示到一個 table view 中時,你均可以複用這些代碼。你也能夠實現一些額外的方法,好比 編碼

tableView:commitEditingStyle:forRowAtIndexPath:複製代碼

多個section

還有一種狀況,若是是多個section的狀況下,咱們還能夠再擴展一下,將block定義爲spa

typedef void(^TableViewCellConfigureBlock)(id cell, id item, NSIndexPath *indexPath);複製代碼
#pragma mark - tableview config cell
- (void)configCell{
    void (^configCell)(PPSFriendCell *, PPSFriend *, NSIndexPath *) = ^(PPSFriendCell *cell, PPSFriend *friend, NSIndexPath *indexPath){
        cell.textLabel.text = friend.name;
    };
    PPSArrayDatasource *datasources = [[PPSArrayDatasource alloc] initWithItems:self.friends cellIdentifier:@"friendCell" configureCellBlock:configCell];
    self.tableView.dataSource = datasources;
}複製代碼

那麼在PPSArrayDatasource的items中裝的應該就是一個一個的數組了,分別對應的每一個section,這裏我只是針對這種狀況說明一下,不論是多個section仍是單個section均可以使用這種方法,來瘦身.net

在 table view controllers 之間共享。

這樣的好處在於,你能夠單獨測試這個類,不再用寫第二遍。該原則一樣適用於數組以外的其餘對象。

將業務邏輯移到model中

下面是在viewcontroller中寫的用來查找一個用戶的目前的優先事項的列表:

- (void)loadPriorities {
    NSDate* now = [NSDate date];
    NSString* formatString = @"startDate = %@";
    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 = %@";
    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 保持小巧。Store 對象會關心數據加載、緩存和設置數據棧。它也常常被稱爲服務層或者倉庫。

把網絡層請求邏輯移到model層

和上面的主題類似:不要在 view controller 中作網絡請求的邏輯。取而代之,你應該將它們封裝到另外一個類中。這樣,你的 view controller 就能夠在以後經過使用回調(好比一個 completion 的 block)來請求網絡了。這樣的好處是,緩存和錯誤控制也能夠在這個類裏面完成。

把View移到View層

不該該在 view controller 中構建複雜的 view 層次結構。你可使用 Interface Builder 或者把 views 封裝到一個 UIView 子類當中。例如,若是你要建立一個選擇日期的控件,把它放到一個名爲 DatePickerView 的類中會比把全部的事情都在 view controller 中作好好得多。再一次,這樣增長了可複用性並保持了簡單。

簡單來講,就是將一個viewcontroller中複雜的view構造,放到一個單獨的view類中,然在viewcontroller中,只須要構建一個這個類就行。

總結

咱們已經看到一些用來建立更小巧的 view controllers 的技術。咱們並非想把這些技術應用到每個可能的角落,只是咱們有一個目標:寫可維護的代碼。知道這些模式後,咱們就更有可能把那些笨重的 view controllers 變得更整潔。

相關文章
相關標籤/搜索