IMP指針的做用

可能你們一直看到有許多朋友在Runtime相關文章中介紹IMP指針的概念,那麼IMP究竟有什麼實際做用呢?讓咱們先從一個函數看起來。
函數

Method Swizzlingspa

若是對Runtime有必定了解的話,必定據說過或者用過這個函數:指針

void method_exchangeImplementations(Method m1, Method m2)

它一般叫作method swizzling,算是ObjC的"黑魔法"了,做用就是在程序運行期間動態的給兩個方法互換實現,好比有這樣一種使用場景:調試

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

有許多朋友會這樣說,讓全部控制器都繼承一個BaseController不就能夠了嗎?我在這裏要解釋一下這樣作的缺點:假如你的項目裏有許多Controller的話,你就須要把項目裏凡是沒有繼承自BaseController的每一個Controller都作一次修改了,並且隨意更改層級結構會發生意想不到的錯誤。orm

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

blob.png

IMP指針編譯器

其實,還有一種更加簡單的方法可讓咱們辦到相同的目的,運用IMP指針,IMP就是Implementation的縮寫,顧名思義,它是指向一個方法實現的指針,每個方法都有一個對應的IMP,因此,咱們能夠直接調用方法的IMP指針,來避免方法調用死循環的問題。it

調用一個IMP的方式和調用普通C函數相同,好比:io

id returnObjc = someIMP(objc,SEL,params...);

不過若是你的項目沒有作其餘配置的話這樣調用編譯器是不會經過的,咱們來看一下先它的定義:

if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
else
typedef id (*IMP)(id, SEL, ...); 
endif

在默認狀況下你的工程是打開這個配置的

blob.png

這種狀況下IMP被定義爲無參數無返回值的函數。因此你須要到工程裏搜索到這個選項並把它關閉。這樣的麻煩就是,每次使用,你都須要修改工程配置,因此這裏我再介紹另一種辦法:從新定義一個和有參數的IMP指針相同的指針類型,在獲取IMP時把它強轉爲此類型。這樣運用IMP指針後,就不須要額外的給ViewController寫新的方法:

blob.png

還有一個地方咱們須要注意,若是這樣直接調用IMP的話就會發生經典的EXC_BAD_ACCESS錯誤,咱們定義的IMP指針是一個有返回值的類型,而其實咱們獲取的viewDidLoad這個方法是沒有返回值的,因此咱們須要新定義一個和IMP相同類型的函數指針好比VIMP,把他的返回值定位Void,這樣若是你修改的方法有返回值就用IMP,沒有返回值就用VIMP。

blob.png

blob.png

值得注意的是,若是你重寫的方法有返回值,不要忘記在最後作return。

總結

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

這是隻是IMP使用的場景之一,它還有許多做用,但願你們多多發現。

相關文章
相關標籤/搜索