runtime系列(二) method swizzling 與AOP編程

什麼是 AOP : (site: baike.baidu.com),引用百度百科中的解釋就是:

在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。

主要功能:

日誌記錄,性能統計,安全控制,事務處理,異常處理等等

主要意圖:

將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,通過對這些行爲的分離,我們希望可以將它們獨立到非指導業務邏輯的方法中,進而改變這些行爲的時候不影響業務邏輯的代碼。

iOS 開發中的 AOP

Objective-C 中,類的方法列表會把選擇器的名稱映射到方法的實現上,這樣 動態消息轉發系統 就可以以此找到需要調用的方法。這些方法是以函數指針的形式來表示,這種指針叫做 IMP

如下:

id (*IMP) (id, SEL, ...)

Objective-Cruntime 機制以此提供了獲取和交換映射 IMP 的的接口:

// 獲取方法
Method class_getInstanceMethod(Class cls, SEL name);

// 交換兩個方法
void method_exchangeImplementations(Method m1, Method m2)

我們可以通過上面兩個方法來進行選擇器和所映射的 IMP 進行交換:

來,直接上代碼示例,比如我們的要實現功能是在每個控制器的 - viewDidLoad 方法裏面log一下,一般有三種實現方式:

  1. 直接修改每個頁面的 view controller 代碼,簡單粗暴;
  2. 子類化 view controller ,並讓我們的 view controller 都繼承這些子類;
  3. 使用 Method Swizzling 進行 hook,以達到 AOP 編程的思想

第一種實現的代碼是在每個類的裏面都這麼寫:

- (void)viewDidLoad {
    [super viewDidLoad];
    DDLog();
}

第二種是隻在基類裏面寫。然後所有的控制器都繼承這個基類。

最後一種是最佳的解決方案:

@implementationUIViewController(Log)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(viewDidLoad);
        SEL swizzledSelector = @selector(log_viewDidLoad);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        if (success) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}





#pragma mark - Method Swizzling - (void)log_viewDidLoad{ [self log_viewDidLoad]; DDLog(...); } @end

注意:

  • 爲什麼使用 + (void)load ?因爲父類、子類和分類的該方法是分別調用,互不影響,而且是在類被加載的時候必定會調用的方法。
    </div>
發佈了24 篇原創文章 · 獲贊 5 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章