iOS控制器瘦身-面向超類編程

今天寫這篇文章的目的絮重,是提供一種思路痕檬,來幫助大家解決控制器非常臃腫的問題霎槐,對(duì)控制器瘦身。

滴滴 老司機(jī)要開車了

如果手邊有項(xiàng)目梦谜,不妨打開工程看一下你的控制器代碼有多少行丘跌,是不是非常多?再看一下tableView的代理方法cellForRow和heightForRow的代碼是不是也是非常多唁桩?里面夾雜著switch和大量if esle的判斷邏輯的代碼闭树。后期維護(hù)看著這些if else是不是特別煩躁?特別是自己在維護(hù)前人寫的代碼荒澡,并且還沒有注釋 一團(tuán)糟蔼啦,是不是有更想罵人的沖動(dòng)?別怕仰猖,這里給您提供一種解決思路捏肢,讓你的tableView代理方法再也沒有這種讓人頭疼的if else判斷邏輯,讓你的控制器代碼量大大減少饥侵,并且后期維護(hù)成本也大大的減少鸵赫。

在說具體解決思路前,先給大家簡單復(fù)習(xí)一下MVC和MVVM,因?yàn)榻裉斓闹黝}也是和MVVM有關(guān)系躏升。MVC模式大家都很熟悉了辩棒,就是Model,View,Controller三層,Model負(fù)責(zé)數(shù)據(jù)層膨疏,Controller負(fù)責(zé)業(yè)務(wù)邏輯層一睁,View負(fù)責(zé)界面顯示層,Model和View通過Controller來實(shí)現(xiàn)橋接交互佃却,程序的擴(kuò)展性很好者吁,好處多多。但是呢饲帅,MVC也有它自身的缺陷复凳,那就是控制器太臃腫,如果你想在控制器中定位某一個(gè)點(diǎn)是比較麻煩的事灶泵。

那么為什么控制器如此龐大育八,就是因?yàn)閠ableView的代理方法里的cell 判斷邏輯全在控制器,以及網(wǎng)絡(luò)請(qǐng)求也在控制器發(fā)起赦邻,另外還有一些其他的業(yè)務(wù)邏輯髓棋。有沒有什么更高的模式呢?MVVM模式就是MVC模式的升級(jí)版惶洲。
MVVM中Model依然負(fù)責(zé)數(shù)據(jù)層,Controller單單負(fù)責(zé)View的展示和更新按声,其他業(yè)務(wù)邏輯不管。View依然負(fù)責(zé)界面顯示湃鹊。那么ViewController之前負(fù)責(zé)的業(yè)務(wù)邏輯現(xiàn)在誰來負(fù)責(zé)呢儒喊?我們?cè)傩陆ㄒ粋€(gè)ViewModel層,處在ViewController層和Model層之間币呵,專門負(fù)責(zé)業(yè)務(wù)邏輯怀愧,以及網(wǎng)絡(luò)請(qǐng)求等任務(wù)。ViewController從ViewModel中獲取數(shù)據(jù)然后顯示在View上余赢,它并不和Model層直接打交道芯义,和Model層直接打交道的是ViewModel層 。
下面附上一張經(jīng)典gif圖片妻柒,幫助大家理解兩者之間的關(guān)系扛拨。
MVC與MVVM的關(guān)系
好了,前面的都是回顧一些相關(guān)知識(shí)举塔,為理解接下來的內(nèi)容做基礎(chǔ)绑警,如果想要深入了解MVC,MVVM可以網(wǎng)上找下求泰,這類文章很多。
大家可能疑惑到底什么是面向超類編程计盒,其實(shí)就是圍繞繼承這個(gè)特性渴频,子類cell繼承父類cell,面向父類這個(gè)對(duì)象來編程北启,最終對(duì)控制器的tableView進(jìn)行瘦身卜朗,也不止是對(duì)tableView優(yōu)化,配合MVVM新建ViewModel可以抽離很大一部分控制器的代碼」敬澹現(xiàn)在還不清楚沒關(guān)系场钉,下面會(huì)有很詳細(xì)的描述讓你明白。_ 我下面先把大家常用的控制器tableView代理方法的寫法懈涛,給黏貼出來逛万,然后再用新的面向超類的寫法給黏貼出來,大家就可以明顯體會(huì)到使用面向超類寫法的好處了肩钠。
老方式大眾寫法.png

面向超類編程寫法.png
看到這里泣港,可能會(huì)有人吐槽了,新寫法就比老式寫法少了十幾行嘛价匠?其實(shí)不是這樣的当纱,首先這個(gè)demo我只寫了三個(gè)cell作為例子,真實(shí)的項(xiàng)目極少一個(gè)控制器只有三個(gè)cell吧踩窖?控制器的cell越多坡氯,好處越明顯, 因?yàn)樵诤笃诓还芴砑佣嗌賑ell洋腮,控制器tableView的代理方法中的代碼幾乎都不會(huì)增加箫柳,相當(dāng)于構(gòu)建了一個(gè)模版,只需要在新添加cell的內(nèi)部配置即可啥供。其次悯恍,真實(shí)的項(xiàng)目也不可能業(yè)務(wù)邏輯這么簡單吧?肯定在if else中嵌套了很多其他的邏輯代碼伙狐,致使tableView看起來很臃腫涮毫。

面向超類編程的好處:
1.控制器瘦身。[控制器內(nèi)部代碼量大幅度減少贷屎,邏輯更加清晰]
2.后期維護(hù)成本大大降低罢防。[后期如果想添加或者刪除cell,只需要新建或者刪除一個(gè)子類cell唉侄,在viewModel中添加或刪除一個(gè)identifier即可咒吐,控制器幾乎不用加任何代碼]
面向超類的壞處:
1.新建更多的cell文件和一個(gè)viewModel文件,包大小會(huì)響應(yīng)增加。

下面就具體講解面向超類編程瘦身大概要做什么:

一恬叹,新建一個(gè)繼承自UITableViewCell的父類cell

#import <UIKit/UIKit.h>
#import "ResponseNewProgrammeData.h"
#import "NewProgrammeCellHeightProtocol.h"

//子類需要有回調(diào)事件的代理
@protocol NewProgrammeTableViewCellProtocol <NSObject>
- (void)cell1DidSelectedRightButton;
- (void)cell2DidSelectedRightButton;
- (void)cell3DidSelectedRightButton;
@end

@interface NewProgrammeBaseCell : UITableViewCell <NewProgrammeCellHeightProtocol>
@property (nonatomic,   weak) id<NewProgrammeTableViewCellProtocol> delegate;
@property (nonatomic, strong) ResponseNewProgrammeData * responseNewProgrammeData;
@end

首先,要包含控制器的數(shù)據(jù)源候生,因?yàn)樽宇恈ell的UI等操作全靠這個(gè)父類的數(shù)據(jù)源。
其次,要實(shí)現(xiàn)NewProgrammeCellHeightProtocol協(xié)議妄呕,作為計(jì)算高度用陶舞,具體用法在第二點(diǎn)講解。
最后,如果子類cell有點(diǎn)擊事件需要回調(diào)操作的绪励,可再寫一個(gè)協(xié)議NewProgrammeTableViewCellProtocol作為屬性持有,在控制器中將delegate指向控制器作為回調(diào)使用唠粥。

二疏魏,新建一個(gè)NewProgrammeCellHeightProtocol

#import <Foundation/Foundation.h>

//針對(duì)cell的高度寫的協(xié)議
@protocol NewProgrammeCellHeightProtocol <NSObject>
@optional
+ (BOOL)isStaticCell;
+ (float)cellHeight;
@end
  • (BOOL)isStaticCell方法是在子類中使用的,如果當(dāng)前cell是高度固定的靜態(tài)cell晤愧,就在返回YES大莫,并且在cellHeight方法中返回固定高度。否則返回NO即可官份,也不需要寫+ (float)cellHeight方法只厘。這兩個(gè)方法會(huì)在控制器的heightForRow方法中使用,計(jì)算當(dāng)前cell高度舅巷。

**三羔味,新建控制器所需要的所有cell,且繼承自剛才的父類cell **

#import "NewProgrammeCell1.h"

@interface NewProgrammeCell1 ()
@property (nonatomic, weak) IBOutlet UILabel *lblName;
@end

@implementation NewProgrammeCell1
@synthesize responseNewProgrammeData = _responseNewProgrammeData;

- (void)setResponseNewProgrammeData:(ResponseNewProgrammeData *)responseNewProgrammeData
{
    _responseNewProgrammeData = responseNewProgrammeData;
    self.lblName.text = _responseNewProgrammeData.string1;
}
+ (BOOL)isStaticCell
{
    return YES;
}
+ (float)cellHeight
{
    return 44;
}
- (IBAction)didPressedPush:(id)sender {
    if (self.delegate && [self.delegate respondsToSelector:@selector(cell1DidSelectedRightButton)]) {
         [self.delegate cell1DidSelectedRightButton];
    }
}
@end

這個(gè)cell的高度是固定靜態(tài)的钠右,所以isStaticCell方法返回YES赋元,cellHeight返回高度。
這個(gè)cell中可以拿到控制器數(shù)據(jù)源飒房,根據(jù)自己的需要去獲取數(shù)據(jù)搁凸。
這個(gè)cell的didPressedPush方法是模擬需要點(diǎn)擊事件的,回調(diào)給控制器狠毯。
其他cell的配置都大致是這樣的护糖。

四,新建viewModel文件

#import "NewProgrammeViewModel.h"

static NSString * const NewProgrammeCell1Identifier = @"NewProgrammeCell1";
static NSString * const NewProgrammeCell2Identifier = @"NewProgrammeCell2";
static NSString * const NewProgrammeCell3Identifier = @"NewProgrammeCell3";

@implementation NewProgrammeViewModel

- (NSArray *)getIdentifierList
{
    return  @[NewProgrammeCell1Identifier,
              NewProgrammeCell2Identifier,
              NewProgrammeCell3Identifier];
}
- (void)requestData
{
    self.responseNewProgrammeData = [[ResponseNewProgrammeData alloc] init];
}

@end

viewModel負(fù)責(zé)配置控制器所需要注冊(cè)的cell以及真正要顯示的cell嚼松,getIdentifierList返回需要注冊(cè)的所有cell嫡良。因?yàn)槟承╉撁娴腸ell不是固定顯示的,可能根據(jù)數(shù)據(jù)源動(dòng)態(tài)的來配置惜颇。同時(shí)皆刺,viewModel也負(fù)責(zé)網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)解析等其他業(yè)務(wù)邏輯代碼。這里的viewModel相當(dāng)于MVVM模式中的胖Model凌摄,不僅處理網(wǎng)絡(luò)請(qǐng)求羡蛾,還處理頁面UI的配置等其他業(yè)務(wù)邏輯,這樣就不會(huì)使控制器那么臃腫锨亏。

五痴怨,在控制器中做相應(yīng)代碼配置

- (void)configTableViewCell
{
    for (NSString * identifer in [self.viewModel getIdentifierList]) {
        [self.tableView registerNib:[UINib nibWithNibName:identifer bundle:nil]  forCellReuseIdentifier:identifer];
    }
}

#pragma mark - UITableViewDelegate

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString * cellIdentifier = [self.viewModel.getIdentifierList objectAtIndex:indexPath.row];
    NewProgrammeBaseCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    cell.delegate = self;
    cell.responseNewProgrammeData = self.viewModel.responseNewProgrammeData;
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString * cellIdentifier  = [self.viewModel.getIdentifierList objectAtIndex:indexPath.row];
    Class<NewProgrammeCellHeightProtocol> cellClass = NSClassFromString(cellIdentifier);
    CGFloat height = 0;
    if ([cellClass isStaticCell]) {
        height  = [cellClass cellHeight];
        return height;
    } else {
        NewProgrammeBaseCell * cell = (NewProgrammeBaseCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];
        height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingExpandedSize].height;
        return height;
    }
}

首先忙干,要實(shí)例化viewModel對(duì)象,在configTableViewCell方法中獲取需要注冊(cè)的所有cell浪藻,遍歷注冊(cè)捐迫。
其次,在cellForRow代理方法中爱葵,從viewModel對(duì)象中獲取所有注冊(cè)的cell的identifier剥懒,然后從tableView中獲取賦值給父類cell。再針對(duì)父類cell做一些賦值操作任内,也就是分別調(diào)用了子類cell揍很,充分利用多態(tài)的特性。
最后辆雾,在heightForRow代理方法中肪笋,仍然是根據(jù)viewModel對(duì)象獲取所有注冊(cè)的cell的identifier,再根據(jù)identifier反射成對(duì)象子類cell的類對(duì)象度迂,接著調(diào)用cell中的協(xié)議方法藤乙,來計(jì)算高度。
到這里惭墓,面向超類編程瘦身基本思路都說完了坛梁,這里有完整的demo點(diǎn)擊下載源碼,如果喜歡動(dòng)下小手給個(gè)star诅妹,謝謝啦??~自己可以在項(xiàng)目中嘗試下罚勾,可能在實(shí)際應(yīng)用中會(huì)有其他沒有想到的問題,比如說吭狡,當(dāng)前控制器的cell最多顯示3個(gè)尖殃,但是在某種情況下是顯示2個(gè),有1個(gè)不需要顯示划煮。那么我們?cè)趘iewModel中應(yīng)該怎么配置getIdentifierList數(shù)據(jù)源呢送丰?我們可以這樣做,獲取最新的數(shù)組。不同的項(xiàng)目業(yè)務(wù)邏輯不一樣弛秋,寫法也會(huì)有差別器躏,具體問題具體分析。

- (NSArray *)getNewIdentifierList
{
    NSMutableArray * newList = [[self getIdentifierList] mutableCopy];
    if (**判斷條件**) {
        [newList removeObject:NewProgrammeCell2Identifier];
    }
    return [newList copy];
}

希望本文的瘦身思路可以幫助您蟹略,如果此文哪里有紕漏登失,或者您有什么更好的建議,歡迎提出來挖炬,大家一塊探討揽浙。iOS開發(fā)技術(shù)交流qq群: 529560119,提供各種最新權(quán)威學(xué)習(xí)書籍及開發(fā)視頻

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市馅巷,隨后出現(xiàn)的幾起案子膛虫,更是在濱河造成了極大的恐慌,老刑警劉巖钓猬,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稍刀,死亡現(xiàn)場離奇詭異,居然都是意外死亡敞曹,警方通過查閱死者的電腦和手機(jī)账月,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來异雁,“玉大人捶障,你說我怎么就攤上這事「俚叮” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵担平,是天一觀的道長示绊。 經(jīng)常有香客問我,道長暂论,這世上最難降的妖魔是什么面褐? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮取胎,結(jié)果婚禮上展哭,老公的妹妹穿的比我還像新娘。我一直安慰自己闻蛀,他們只是感情好匪傍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著觉痛,像睡著了一般役衡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上薪棒,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天手蝎,我揣著相機(jī)與錄音,去河邊找鬼俐芯。 笑死棵介,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吧史。 我是一名探鬼主播邮辽,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了逆巍?” 一聲冷哼從身側(cè)響起及塘,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锐极,沒想到半個(gè)月后笙僚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灵再,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年肋层,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翎迁。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡栋猖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出汪榔,到底是詐尸還是另有隱情蒲拉,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布痴腌,位于F島的核電站雌团,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏士聪。R本人自食惡果不足惜锦援,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望剥悟。 院中可真熱鬧灵寺,春花似錦、人聲如沸区岗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽躏尉。三九已至蚯根,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胀糜,已是汗流浹背颅拦。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留教藻,地道東北人距帅。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像括堤,于是被迫代替她去往敵國和親碌秸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绍移,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容