Swift_技巧(3)_Aspects切面編程應(yīng)用

一丶介紹

Aspect Oriented Programming(AOP),面向切面編程

AOP主要實現(xiàn)的目的是針對業(yè)務(wù)處理過程中的切面進行提取,它所面對的是處理過程中的某個步驟或階段,以獲得邏輯過程中各部分之間低耦合性的隔離效果。

[1] 比如我們最常見的就是日志記錄了,舉個例子,我們現(xiàn)在提供一個服務(wù)查詢學生信息的,但是我們希望記錄有誰進行了這個查詢。如果按照傳統(tǒng)的OOP的實現(xiàn)的話,那我們實現(xiàn)了一個查詢學生信息的服務(wù)接口(StudentInfoService)和其實現(xiàn)類(StudentInfoServiceImpl.java),同時為了要進行記錄的話,那我們在實現(xiàn)類(StudentInfoServiceImpl.java)中要添加其實現(xiàn)記錄的過程。這樣的話,假如我們要實現(xiàn)的服務(wù)有多個呢?那就要在每個實現(xiàn)的類都添加這些記錄過程。這樣做的話就會有點繁瑣,而且每個實現(xiàn)類都與記錄服務(wù)日志的行為緊耦合,違反了面向?qū)ο蟮囊?guī)則。那么怎樣才能把記錄服務(wù)的行為與業(yè)務(wù)處理過程中分離出來呢?看起來好像就是查詢學生的服務(wù)自己在進行,但卻是背后日志記錄對這些行為進行記錄,并且查詢學生的服務(wù)不知道存在這些記錄過程,這就是我們要討論AOP的目的所在。AOP的編程,好像就是把我們在某個方面的功能提出來與一批對象進行隔離,這樣與一批對象之間降低了耦合性,可以就某個功能進行編程。
---摘自百度百科

我們要用AOP來做什么呢?
1.搭建Control的時候,一般會寫個BaseViewController,然后把相同功能的代碼放在相同的函數(shù)內(nèi)比如以下:

- (void)viewDidLoad
{
    [super viewDidLoad];
    //創(chuàng)建視圖代碼
    [self createUI];
   //初始化數(shù)據(jù)
    [self initdata];
   //網(wǎng)絡(luò)請求
    [self askNetwork];
}

規(guī)范代碼,可以減少不同開發(fā)者之間溝通成本,以及提高問題的定位速度,減少解決時間;
鑒于BaseViewController太臃腫,有了aop編程,就有了新的解決方案;

二丶Aspects的介紹

OC 有一個成熟的aspect方案->Aspects
地址:https://github.com/steipete/Aspects
源碼解析及應(yīng)用:http://wereadteam.github.io/2016/06/30/Aspects/

主要還是用到oc神奇的runtime機制,動態(tài)的改變了 selector 和 IMP 的對應(yīng)關(guān)系;(此圖并非原創(chuàng))

Paste_Image.png

主要用到2個方法:

+ (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í)行的時機

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其實不用BaseViewcontrol也是可以的;只要導(dǎo)入#import "UIViewController+Base.h" 文件就可以;優(yōu)化不是一點點;

四丶Swift 實現(xiàn)

首先要先知道幾個東西;
1.swift 沒有l(wèi)oad方法,使用initialize()
2.@convention關(guān)鍵字的作用:
2.1 修飾 Swift 中的函數(shù)類型,調(diào)用 C 的函數(shù)時候,可以傳入修飾過 @convention(c) 的函數(shù)類型,匹配 C 函數(shù)參數(shù)中的函數(shù)指針。
2.2 修飾 Swift 中的函數(shù)類型,調(diào)用 Objective-C 的方法時候,可以傳入修飾過 @convention(block) 的函數(shù)類型,匹配 Objective-C 方法參數(shù)中的 block 參數(shù)
3.unsafeBitCast
unsafeBitCast是非常危險的操作,它會將一個指針指向的內(nèi)存強制按位轉(zhuǎn)換為目標的類型。因為這種轉(zhuǎn)換是在Swift的類型管理之外進行的,因此編譯器無法確保得到的類型是否確實正確,你必須明確地知道你在做什么

4.需要把原先填寫block的參數(shù),轉(zhuǎn)成AnyObject

具體代碼:

override public class func initialize() {
    /*
     @convention
     1. 修飾 Swift 中的函數(shù)類型,調(diào)用 C 的函數(shù)時候,可以傳入修飾過 @convention(c) 的函數(shù)類型,匹配 C 函數(shù)參數(shù)中的函數(shù)指針。
     2. 修飾 Swift 中的函數(shù)類型,調(diào)用 Objective-C 的方法時候,可以傳入修飾過 @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)")
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,034評論 25 709
  • 前言 在“Runtime病院”住院的后兩天,分析了一下AOP的實現(xiàn)原理?!俺鲈骸焙?,發(fā)現(xiàn)Aspect庫還沒有詳細分...
    一縷殤流化隱半邊冰霜閱讀 18,807評論 34 165
  • 對沒有辨識力的人來說,自我的爬山和無私的爬山看上去可能都一樣,都是一步一步地往上爬;呼吸的速度也一樣;疲累的時候都...
    余江燕閱讀 281評論 0 0
  • 酒瘋子 /深山老林(千年桃妖) 見酒沒命 遇酒即醉 有時嗨飲 沒時 一朝 心荒荒 即便今日囊中羞 借錢也要打幾兩 ...
    深山老林千年桃妖閱讀 199評論 2 1
  • 記憶中對手工制品最開始的印象大概就是老物件吧,結(jié)實,耐用。長大后發(fā)現(xiàn)原來手工制品意味著更多的money,是因為現(xiàn)在...
    大米996閱讀 307評論 0 1

友情鏈接更多精彩內(nèi)容