void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector)
{
// the method might not exist in the class, but in its superclass
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// class_addMethod will fail if original method already exists
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
// the method doesn’t exist and we just added one
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
上面這個(gè)C風(fēng)格函數(shù),就是SwizzleMethod的核心方法,用來(lái)交換Runtime中類和對(duì)象的方法接口指針。但是這有什么用呢?
你知道有名的第三方庫(kù)IQKeyboard么?
這個(gè)吊庫(kù),不需要引入頭文件,不需要調(diào)用任何方法就能使用。怎么做到的呢?
答案是NSObject的 + (void)load
方法。
這個(gè)類方法,在軟件運(yùn)行時(shí)一定會(huì)調(diào)用一次,并且不需要調(diào)用super方法,因?yàn)楦割惖膌oad方法也一定會(huì)調(diào)用。
IQKeyboard就是在load方法中初始化的。
SwizzleMethod應(yīng)用實(shí)例 —— 無(wú)痛手術(shù)
這個(gè)比喻并不準(zhǔn)確,準(zhǔn)確說(shuō)應(yīng)該是無(wú)痕手術(shù) —— 對(duì)方法的無(wú)痕手術(shù)
+ (void)load
{
swizzleMethod([AppDelegate class], @selector(application:didFinishLaunchingWithOptions:), @selector(swizzle_application:didFinishLaunchingWithOptions:));
}
這里,我們把AppDelegate的啟動(dòng)方法更換成了我們自己的swizzle_application:didFinishLaunchingWithOptions方法。兩個(gè)方法指針互換,然后我們?cè)谖覀兊姆椒ㄖ屑尤胛覀冃枰拇a。
- (BOOL)swizzle_application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//我們需要添加的代碼
//
return [self swizzle_application:application didFinishLaunchingWithOptions:launchOptions];
}
注意到了么,結(jié)尾我們自己調(diào)用swizzle_application:application
方法,因?yàn)檫@個(gè)這個(gè)方法指針實(shí)際已經(jīng)指向AppDelegate
的application:didFinishLaunchingWithOptions
方法。其他地方掉用AppDelegate
的application:didFinishLaunchingWithOptions
方法則會(huì)指向我們的swizzle_application:application
方法,這樣我們就在人不知不覺(jué)中,向AppDelegate
插入了一段代碼,這一切不需要AppDelegate引入任何頭文件,是不是很Cool?
這樣一來(lái)就可以把需要放在這里面的各種監(jiān)測(cè)代碼初始化,都放到我們的swizzle_application:application
方法中,可以給這個(gè)方法新建一個(gè)類,每次新建工程直接拖進(jìn)來(lái)一起編譯,分分鐘植入,帥爆一切,點(diǎn)個(gè)贊吧。