一、寫在前面
前些時(shí)間聽朋友說(shuō)了一個(gè)話題,利用 AOP 解耦細(xì)節(jié)業(yè)務(wù),確實(shí)有趣。因?yàn)槲覀兺ǔG闆r下說(shuō)起 AOP,都會(huì)想起比如“埋點(diǎn)”、“method swizzing”等字眼,角度比較宏觀。AOP 國(guó)內(nèi)開發(fā)者喜歡稱之為面向切面編程,其作為面向?qū)ο缶幊痰囊环N補(bǔ)充,在實(shí)際業(yè)務(wù)場(chǎng)景中發(fā)揮著巨大作用。
二、為什么使用 AOP
面向切面編程,也可以理解為面向功能面編程,將某一特定的功能視為一個(gè)切面,不但可以復(fù)用代碼,還可以使代碼邏輯更加清晰,更符合單一功能設(shè)計(jì)原則。
在 iOS 開發(fā)中,經(jīng)常會(huì)有這種需求,比如需要記錄進(jìn)入每一個(gè)控制器的次數(shù)。最次的方案就是在每一個(gè)控制器的viewWillApear:方法里面寫記錄代碼;稍優(yōu)一點(diǎn)的方案是在基類的viewWillApear:里面寫記錄代碼,但是這種方法有個(gè)弊端就是只有繼承于基類的控制器才能記錄到,不友好;而最優(yōu)的方式就是利用runtime的method swizzing交換方法,hook住viewWillApear:在里面做記錄邏輯。
當(dāng)然,本文是為了解決另外一個(gè)問(wèn)題。
在 iOS App 中,MVC 和 MVVM 是比較流行的架構(gòu)模式,而當(dāng)某個(gè)界面業(yè)務(wù)量達(dá)到一個(gè)程度過(guò)后,MVVM 甚至是 VIPER 模式都顯得有些力不從心,為了達(dá)到更高層次的解耦,往往會(huì)做其他方面的工作,比如講Scrollview等代理的配置獨(dú)立出來(lái),然而這種方式仍然有個(gè)弊端,那就是代理方法里面的邏輯太多導(dǎo)致獨(dú)立出來(lái)的類仍然臃腫。
所以,這就是寫這篇文章的目的,提供一種更深層次的解耦的方案。

三、實(shí)際應(yīng)用
其實(shí)之前我對(duì) AOP 的思想還不是很了解,后來(lái)發(fā)現(xiàn)其實(shí)我已經(jīng)在之前的框架中有了應(yīng)用,實(shí)現(xiàn)該架構(gòu)實(shí)現(xiàn)的主要技術(shù)點(diǎn)是:利用方法重定向?qū)崿F(xiàn)多接收者的方法轉(zhuǎn)發(fā)。
詳情可看這篇文章,文章中間部分有對(duì)消息轉(zhuǎn)發(fā)流程的簡(jiǎn)述:
iOS解決方案:文本輸入控制(獻(xiàn)上框架)
本文就不講解消息發(fā)送機(jī)制了,在 Demo 中有封裝 ——YBAOPManager,我們將利用它來(lái)做局部解耦。
在實(shí)際業(yè)務(wù)需求中,出場(chǎng)率很高的是UITalbeView和UICollecitonView等需要用大量代理方法配置的視圖,當(dāng)然這是蘋果程序設(shè)計(jì)的慣例。當(dāng)UI界面很復(fù)雜,業(yè)務(wù)邏輯相當(dāng)多的時(shí)候,雖然把網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)處理、視圖封裝等都解耦出去了,但是配置代理里面的邏輯太多,我們想要每一個(gè)類處理一部分代理方法。
Demo 以 UITableView 為例。
首先,創(chuàng)建實(shí)現(xiàn) UITableView 代理的三個(gè)類:
@implementation?TestTableViewDigitConfig
-?(NSInteger)tableView:(UITableView?*)tableView?numberOfRowsInSection:(NSInteger)section?{
????return?20;
}
-?(CGFloat)tableView:(UITableView?*)tableView?heightForRowAtIndexPath:(NSIndexPath?*)indexPath?{
????return?80;
}
@end
@implementation?TestTableViewClickConfig
-?(void)tableView:(UITableView?*)tableView?didSelectRowAtIndexPath:(NSIndexPath?*)indexPath?{
????NSLog(@"click?--?section:%ld,?row:%ld",?indexPath.section,?indexPath.row);
}
@end
@implementation?TestTableViewCellConfig
-?(UITableViewCell?*)tableView:(UITableView?*)tableView?cellForRowAtIndexPath:(NSIndexPath?*)indexPath?{
????UITableViewCell?*cell?=?[tableView?dequeueReusableCellWithIdentifier:NSStringFromClass(UITableViewCell.class)];
????if?(!cell)?{
????????cell.selectionStyle?=?UITableViewCellSelectionStyleNone;
????????cell?=?[[UITableViewCell?alloc]?initWithStyle:UITableViewCellStyleValue1?reuseIdentifier:NSStringFromClass(UITableViewCell.class)];
????}
????cell.textLabel.text?=?[NSString?stringWithFormat:@"第%ld行",?indexPath.row];
????return?cell;
}
@end
如代碼所見(jiàn),這里將 tableView 的代理用三個(gè)類來(lái)分別實(shí)現(xiàn),然后在 UIViewController 里面只需要寫這些代碼:
@interface?TestVC?()
@property?(nonatomic,?strong)?UITableView?*tableView;
@property?(nonatomic,?strong)?YBAOPManager?*aopManager;
@property?(nonatomic,?strong)?TestTableViewDigitConfig?*digitConfig;
@property?(nonatomic,?strong)?TestTableViewClickConfig?*clickConfig;
@property?(nonatomic,?strong)?TestTableViewCellConfig?*cellConfig;
@end
@implementation?TestVC
#pragma?mark?life?cycle
-?(void)viewDidLoad?{
????[super?viewDidLoad];
????[self.view?addSubview:self.tableView];
}
#pragma?mark?getter
-?(UITableView?*)tableView?{
????if?(!_tableView)?{
????????_tableView?=?[[UITableView?alloc]?initWithFrame:[UIScreen?mainScreen].bounds?style:UITableViewStylePlain];
????????_tableView.tableFooterView?=?[UIView?new];
????????_digitConfig?=?[TestTableViewDigitConfig?new];
????????_clickConfig?=?[TestTableViewClickConfig?new];
????????_cellConfig?=?[TestTableViewCellConfig?new];
????????_aopManager?=?[YBAOPManager?new];
????????[_aopManager?addTarget:_digitConfig];
????????[_aopManager?addTarget:_clickConfig];
????????[_aopManager?addTarget:_cellConfig];
????????_tableView.delegate?=?_aopManager;
????????_tableView.dataSource?=?_aopManager;
????}
????return?_tableView;
}
@end
核心代碼就是將YBAOPManager類的使用:
當(dāng)你需要使用多個(gè)對(duì)象(target)來(lái)承接一些方法的實(shí)現(xiàn),初始化 YBAOPManager 實(shí)例,將這些對(duì)象實(shí)例添加到
YBAOPManager 實(shí)例中(addTarget),最后將 YBAOPManager 實(shí)例作為這些方法的第一承接者。剩下的方法分發(fā)工作就交給該類了。