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