一丶介紹
Aspect Oriented Programming(AOP)帜乞,面向切面編程
AOP主要實(shí)現(xiàn)的目的是針對(duì)業(yè)務(wù)處理過程中的切面進(jìn)行提取捡多,它所面對(duì)的是處理過程中的某個(gè)步驟或階段蓖康,以獲得邏輯過程中各部分之間低耦合性的隔離效果。
[1] 比如我們最常見的就是日志記錄了垒手,舉個(gè)例子蒜焊,我們現(xiàn)在提供一個(gè)服務(wù)查詢學(xué)生信息的,但是我們希望記錄有誰進(jìn)行了這個(gè)查詢科贬。如果按照傳統(tǒng)的OOP的實(shí)現(xiàn)的話泳梆,那我們實(shí)現(xiàn)了一個(gè)查詢學(xué)生信息的服務(wù)接口(StudentInfoService)和其實(shí)現(xiàn)類(StudentInfoServiceImpl.java),同時(shí)為了要進(jìn)行記錄的話唆迁,那我們?cè)趯?shí)現(xiàn)類(StudentInfoServiceImpl.java)中要添加其實(shí)現(xiàn)記錄的過程鸭丛。這樣的話竞穷,假如我們要實(shí)現(xiàn)的服務(wù)有多個(gè)呢唐责?那就要在每個(gè)實(shí)現(xiàn)的類都添加這些記錄過程。這樣做的話就會(huì)有點(diǎn)繁瑣瘾带,而且每個(gè)實(shí)現(xiàn)類都與記錄服務(wù)日志的行為緊耦合鼠哥,違反了面向?qū)ο蟮囊?guī)則。那么怎樣才能把記錄服務(wù)的行為與業(yè)務(wù)處理過程中分離出來呢看政?看起來好像就是查詢學(xué)生的服務(wù)自己在進(jìn)行朴恳,但卻是背后日志記錄對(duì)這些行為進(jìn)行記錄,并且查詢學(xué)生的服務(wù)不知道存在這些記錄過程允蚣,這就是我們要討論AOP的目的所在于颖。AOP的編程,好像就是把我們?cè)谀硞€(gè)方面的功能提出來與一批對(duì)象進(jìn)行隔離嚷兔,這樣與一批對(duì)象之間降低了耦合性森渐,可以就某個(gè)功能進(jìn)行編程。
---摘自百度百科
我們要用AOP來做什么呢?
1.搭建Control的時(shí)候,一般會(huì)寫個(gè)BaseViewController,然后把相同功能的代碼放在相同的函數(shù)內(nèi)比如以下:
- (void)viewDidLoad
{
[super viewDidLoad];
//創(chuàng)建視圖代碼
[self createUI];
//初始化數(shù)據(jù)
[self initdata];
//網(wǎng)絡(luò)請(qǐng)求
[self askNetwork];
}
規(guī)范代碼,可以減少不同開發(fā)者之間溝通成本,以及提高問題的定位速度,減少解決時(shí)間;
鑒于BaseViewController太臃腫,有了aop編程,就有了新的解決方案;
二丶Aspects的介紹
OC 有一個(gè)成熟的aspect方案->Aspects
地址:https://github.com/steipete/Aspects
源碼解析及應(yīng)用:http://wereadteam.github.io/2016/06/30/Aspects/
主要還是用到oc神奇的runtime機(jī)制,動(dòng)態(tài)的改變了 selector 和 IMP 的對(duì)應(yīng)關(guān)系;(此圖并非原創(chuàng))
主要用到2個(gè)方法:
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;
/// Adds a block of code before/instead/after the current `selector` for a specific instance.
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;
block 執(zhí)行的時(shí)機(jī)
typedef NS_OPTIONS(NSUInteger, AspectOptions) {
AspectPositionAfter = 0, /// Called after the original implementation (default)
AspectPositionInstead = 1, /// Will replace the original implementation.
AspectPositionBefore = 2, /// Called before the original implementation.
AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.
};
三丶Aspect OC應(yīng)用
@implementation UIViewController (Base)
+ (void)load
{
NSError *error = nil;
[self aspect_hookSelector:@selector(viewDidLoad) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> aspectInfo){
UIViewController *baseVc = [aspectInfo instance];
[baseVc createUI];
[baseVc initdata];
[baseVc askNetwork];
}error:&error];
if (error)
{
Log(@"Load error: %@",error);
}
}
@end
這么寫成Categorys其實(shí)不用BaseViewcontrol也是可以的;只要導(dǎo)入#import "UIViewController+Base.h" 文件就可以;優(yōu)化不是一點(diǎn)點(diǎn);
四丶Swift 實(shí)現(xiàn)
首先要先知道幾個(gè)東西;
1.swift 沒有l(wèi)oad方法,使用initialize()
2.@convention關(guān)鍵字的作用:
2.1 修飾 Swift 中的函數(shù)類型冒晰,調(diào)用 C 的函數(shù)時(shí)候同衣,可以傳入修飾過 @convention(c) 的函數(shù)類型,匹配 C 函數(shù)參數(shù)中的函數(shù)指針壶运。
2.2 修飾 Swift 中的函數(shù)類型耐齐,調(diào)用 Objective-C 的方法時(shí)候,可以傳入修飾過 @convention(block) 的函數(shù)類型,匹配 Objective-C 方法參數(shù)中的 block 參數(shù)
3.unsafeBitCast
unsafeBitCast是非常危險(xiǎn)的操作埠况,它會(huì)將一個(gè)指針指向的內(nèi)存強(qiáng)制按位轉(zhuǎn)換為目標(biāo)的類型耸携。因?yàn)檫@種轉(zhuǎn)換是在Swift的類型管理之外進(jìn)行的,因此編譯器無法確保得到的類型是否確實(shí)正確询枚,你必須明確地知道你在做什么
4.需要把原先填寫block的參數(shù),轉(zhuǎn)成AnyObject
具體代碼:
override public class func initialize() {
/*
@convention
1. 修飾 Swift 中的函數(shù)類型违帆,調(diào)用 C 的函數(shù)時(shí)候,可以傳入修飾過 @convention(c) 的函數(shù)類型金蜀,匹配 C 函數(shù)參數(shù)中的函數(shù)指針刷后。
2. 修飾 Swift 中的函數(shù)類型,調(diào)用 Objective-C 的方法時(shí)候渊抄,可以傳入修飾過 @convention(block) 的函數(shù)類型尝胆,匹配 Objective-C 方法參數(shù)中的 block 參數(shù)
*/
let block: @convention(block) (AnyObject!) -> Void = {
info in
let aspectInfo = info as! AspectInfo
let control = aspectInfo.instance()
#需要判類,
if let myVc = control as? BaseViewController{
myVc.customView()
myVc.createUI()
myVc.askNetwork()
}
}
#block轉(zhuǎn)AnyObject
let blobj: AnyObject = unsafeBitCast(block, to: AnyObject.self)
do {
let originalSelector = NSSelectorFromString("viewDidLoad")
#在viewDidLoad之后調(diào)用
try UIViewController.aspect_hook(originalSelector, with: .positionBefore, usingBlock: blobj)
} catch {
print("error = \(error)")
}
}