runtime很早以前就聽說(shuō)過(guò),也試著寫過(guò)一些demo,但是總感覺在應(yīng)用中用不上,或者說(shuō)不知道怎么使用;最初接觸的是叫Method Swizzling的東西,下面先講講Method Swizzling;
編程有一種叫做面向切片編程AOP,具體可以定義可以去wiki查看,大概就是把一些碎片的代碼跟主要功能分離;有個(gè)比較好的例子(這例子比較深刻)就是點(diǎn)擊按鈕或者進(jìn)入某些特定界面,將這些用戶習(xí)慣發(fā)送到服務(wù)器做大數(shù)據(jù),這些邏輯實(shí)際上與應(yīng)用邏輯沒關(guān)系,所以讓他們占據(jù)在整個(gè)app中顯得不合適,具體看看實(shí)例:
Method Swizzling原理
每個(gè)類都維護(hù)一個(gè)方法(Method)列表,Method則包含SEL和其對(duì)應(yīng)IMP的信息,方法交換做的事情就是把SEL和IMP的對(duì)應(yīng)關(guān)系斷開,并和新的IMP生成對(duì)應(yīng)關(guān)系。
交換前:Asel->AImp Bsel->BImp
交換后:Asel->BImp Bsel->AImp
實(shí)操:
引入runtime,創(chuàng)建button實(shí)例
#import <objc/runtime.h>
@interface UIButton (ButtonSwizzling)
@end
.m文件實(shí)現(xiàn):
#import "ButtonSwizzling.h"
@implementation UIButton (ButtonSwizzling)
//應(yīng)該盡可能在+load方法中實(shí)現(xiàn),這樣可以保證方法一定會(huì)調(diào)用且不會(huì)出現(xiàn)異常
+(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
Class selfClass = [self class];
SEL originSEL = @selector(endTrackingWithTouch:withEvent:);
//獲取SEL的方法
Method originMethod = class_getInstanceMethod(selfClass, originSEL);
SEL newSEL = @selector(newendTrackingWithTouch:withEvent:);
Method newMethod = class_getInstanceMethod(selfClass, newSEL);
//class_addMethod是給方法添加實(shí)現(xiàn);getImplementation是獲取方法的實(shí)現(xiàn);getTypeEncoding獲取實(shí)現(xiàn)的編碼類型;
//先嘗試給源方法添加實(shí)現(xiàn),這里是為了避免源方法沒有實(shí)現(xiàn)的情況
bool succ = class_addMethod(selfClass, newSEL, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
if (succ) {
//取代實(shí)現(xiàn)
//添加成功:將源方法的實(shí)現(xiàn)替換到交換方法的實(shí)現(xiàn)
class_replaceMethod(selfClass, newSEL, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
} else {
//交換兩個(gè)方法的實(shí)現(xiàn)
//添加失?。赫f(shuō)明源方法已經(jīng)有實(shí)現(xiàn),直接將兩個(gè)方法的實(shí)現(xiàn)交換即可
method_exchangeImplementations(originMethod, newMethod);
}
});
}
- (void)newendTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)event {
NSLog(@"BUTTON 方法改變啦");
//事實(shí)上這里不是死循環(huán)哦,已經(jīng)被替換成sendAction:to:forEvent:
[self newendTrackingWithTouch:touch withEvent:event];
}
@end
這樣你點(diǎn)擊button能看到console輸出"BUTTON 方法改變啦"!!
封裝swizzling有個(gè)很出名的庫(kù),叫ASPECT!有興趣的可以去看看,不過(guò)感覺也沒差