iOS中IMP指針的運用

iOS中若是對Runtime有必定了解的話,必定據說過或者用過這個函數 
void method_exchangeImplementations(Method m1, Method m2),它一般就是所說的method swizzling,算是ObjC的」黑魔法」了,做用就是在程序運行期間動態的給兩個方法互換實現;ios

好比: 
程序中有許多個ViewController,我想在對項目改動最小的狀況下,在當每一個Controller執行完ViewDidLoad之後就在控制檯把本身的名字打印出來,方便去作調試或者瞭解項目結構函數

其實咱們的目的就是重寫ViewDidLoad的方法,並在他的方法最後加上幾句Log,因此咱們須要給UIViewController創建一個category,由於咱們知道,若是在Catagory中重寫一個方法,就會覆蓋它的原有方法實現,可是,這樣作之後就沒有辦法調用系統原有的方法,由於在一個方法裏調用本身的方法會是一個死循環。因此咱們的解決辦法就是,另外寫一個方法來和viewDidLoad「交換」,這樣外部調用viewDidLoad就會調到新建的這個方法中,一樣,咱們調用新建的方法就會調用到系統的viewDidLoad中了spa

第一種方案:.net

#import "UIViewController+viewDidLoad.h"
#import <objc/runtime.h>

@implementation UIViewController (viewDidLoad)

+ (void)load
{
    //保證交換方法只執行一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        //獲取這個類的viewDidLoad方法,它的類型是一個objc_moethod結構體的指針
        Method viewDidLoad = class_getInstanceMethod(self, @selector(viewDidLoad));

        //獲取自定義的方法
        Method viewDidLoaded = class_getInstanceMethod(self, @selector(viewDidUnload));

        //互換兩個方法實現
        method_exchangeImplementations(viewDidLoad, viewDidLoaded);

    });
}

//新建一個方法與viewDidload交換
- (void)viewDidLoaded
{
    [self viewDidLoaded];

    NSLog(@"%@ did load",self);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

第二種方案:指針

IMP 它是一個指向方法實現的指針,每個方法都一個對應的IMP指針。咱們能夠直接調用方法的IMP指針,來避免方法調用死循環的問題調試

//修改的方法有返回值就用IMP,無返回值就用VIMP
typedef id   (*_IMP)  (id,SEL,...);
typedef void (*_VIMP) (id,SEL,...);

#import "UIViewController+viewDidLoad.h"
#import <objc/runtime.h>

@implementation UIViewController (viewDidLoad)

+ (void)load
{
    //保證交換方法只執行一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        //獲取原始方法
        Method viewDidLoad = class_getInstanceMethod(self, @selector(viewDidLoad));

        //獲取方法實現
        _VIMP viewDidLoad_IMP = (_VIMP)method_getImplementation(viewDidLoad);

        //從新設置方法實現
        method_setImplementation(viewDidLoad,imp_implementationWithBlock(^(id target,SEL action){
            viewDidLoad_IMP(target,@selector(viewDidLoad));

            //自定義代碼
            NSLog(@"%@ did load",target);
        }));
    });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

須要注意事項

修改的方法有返回值就用IMP,沒有返回值就用VIMP。重寫的方法有返回值,不要忘記在最後作returncode

實際上直接調用一個方法的IMP指針的效率是高於調用方法自己的,若是有一個合適的時機獲取到方法的IMP的話,能夠試着調用IMP而不用調用方法。get

相關文章
相關標籤/搜索