攔截任意object-c類的函式
阿新 • • 發佈:2018-11-21
一種有缺點的方法
想攔截object-c類的函式,有一種方法是額外寫擴充套件類,例如下面程式碼:
@interface UIView(fordebug)
- (void)removeFromSuperview;
@end
@implementation UIView(fordebug)
- (void)removeFromSuperview{
int abcd = 2134;
// [super removeFromSuperview];
}
@end
以上程式碼丟到你的工程任意的m檔案中,就可以攔截UIView.removeFromSuperView函式的呼叫了。
但是!有一個缺點,
對於私有函式,hook的時候沒法呼叫原函式,例如上面程式碼的註釋行,編譯器會報錯,因為UIView.removeFromSuperView並沒有公開在UIView中,而是宣告在UIView(XXX)的擴充套件類中。
怎麼樣才能攔截任意類的函式呢?
徹底支援hook的方法
下面介紹另外一種方法。
1. 首先,新增一個新函式作為攔截時候相應的函式:
static void imp_processViewWillAppear(id self, SEL cmd, BOOL animated){ //先執行原來的方法 SEL oriSel = sel_getUid("hook_viewWillAppear:"); void (*hook_viewWillAppear)(id, SEL, BOOL) = (void (*)(id,SEL,BOOL))[UIViewController instanceMethodForSelector:oriSel];//函式指標 hook_viewWillAppear(self,cmd,animated); do sth。。。 }
2. 然後讓被hook的函式指向新函式
methodExchange("UIViewController", "viewWillAppear:", "hook_viewWillAppear:", (IMP)imp_processViewWillAppear);
其中,methodExchange程式碼:
void methodExchange(const char *className, const char *originalMethodName, const char *replacementMethodName, IMP imp) { Class cls = objc_getClass(className);//得到指定類的類定義 SEL oriSEL = sel_getUid(originalMethodName);//把originalMethodName註冊到RunTime系統中 Method oriMethod = class_getInstanceMethod(cls, oriSEL);//獲取例項方法 struct objc_method_description *desc = method_getDescription(oriMethod);//獲得指定方法的描述 assert(desc != NULL); if (desc->types) { SEL buSel = sel_registerName(replacementMethodName);//把replacementMethodName註冊到RunTime系統中 if (class_addMethod(cls, buSel, imp, desc->types)) {//通過執行時,把方法動態新增到類中 Method buMethod = class_getInstanceMethod(cls, buSel);//獲取例項方法 method_exchangeImplementations(oriMethod, buMethod);//交換方法 } } }
如此,就徹底實現了任意類的攔截響應。
有沒有更好的?
個人推理,以上兩種方法應該是可以結合使用,例如正常的對於私有的函式,再使用以下方法進行呼叫原函式:
@interface UIView(fordebug)
- (void)removeFromSuperview;
@end
@implementation UIView(fordebug)
- (void)removeFromSuperview{
int abcd = 2134;
// [super removeFromSuperview];
執行原來的方法
SEL oriSel = sel_getUid("removeFromSuperview");
void (*hook_removeFromSuperview)(id, SEL, BOOL) = (void (*)(id,SEL,BOOL))[UIView instanceMethodForSelector:oriSel];//函式指標
hook_removeFromSuperview(self,cmd);
}
@end
不過這種方法沒驗證過,理論上評估是可以,讀者可以自行試試,
有問題,隨時聯絡,加扣扣群,或者留言。
本文結束。