版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2017.07.27 |
前言
OC是運(yùn)行時(shí)的語(yǔ)言贵试,底層就是運(yùn)行時(shí),可以說(shuō)runtime是OC的底層体捏,很多事情也都可以用運(yùn)行時(shí)解決呆馁,下面就講述一下運(yùn)行時(shí)runtime的知識(shí)以及它的妙用。感興趣的可以看上面幾篇鹊漠。
1. 運(yùn)行時(shí)runtime深度解析(一)—— API
Method Swizzling
Method Swizzing
是發(fā)生在運(yùn)行時(shí)的主到,主要用于在運(yùn)行時(shí)將兩個(gè)Method
進(jìn)行交換,我們可以將Method Swizzling
代碼寫(xiě)到任何地方躯概,但是只有在這段Method Swilzzling
代碼執(zhí)行完畢之后互換才起作用登钥。而且Method Swizzling
也是iOS中AOP
(面相切面編程)的一種實(shí)現(xiàn)方式,我們可以利用蘋(píng)果這一特性來(lái)實(shí)現(xiàn)AOP
編程娶靡。
在OC語(yǔ)言的runtime特性中牧牢,調(diào)用一個(gè)對(duì)象的方法就是給這個(gè)對(duì)象發(fā)送消息。是通過(guò)查找接收消息對(duì)象的方法列表,從方法列表中查找對(duì)應(yīng)的SEL塔鳍,這個(gè)SEL對(duì)應(yīng)著一個(gè)IMP(一個(gè)IMP可以對(duì)應(yīng)多個(gè)SEL)伯铣,通過(guò)這個(gè)IMP找到對(duì)應(yīng)的方法調(diào)用。在每個(gè)類(lèi)中都有一個(gè)Dispatch Table
轮纫,這個(gè)Dispatch Table本質(zhì)是將類(lèi)中的SEL和IMP(可以理解為函數(shù)指針)進(jìn)行對(duì)應(yīng)腔寡。而我們的Method Swizzling就是對(duì)這個(gè)table進(jìn)行了操作,讓SEL對(duì)應(yīng)另一個(gè)IMP掌唾。
下面看其原理圖放前。
1. 方法互換在頁(yè)面統(tǒng)計(jì)上的應(yīng)用需求
很多公司都有頁(yè)面統(tǒng)計(jì)這個(gè)需求,這里我們也做一下統(tǒng)計(jì)糯彬,主要有兩種思路:
- 在每一個(gè)控制器中
viewDidLoad
方法中統(tǒng)計(jì)用戶(hù)進(jìn)入控制器的次數(shù)凭语,并上報(bào)至服務(wù)器。但是這有個(gè)缺點(diǎn)就是每一個(gè)控制器都要加很是繁瑣撩扒。 - 還有一種辦法就是寫(xiě)一個(gè)
UIViewController
的分類(lèi)Category
似扔,然后在Category
中的+(void)load
方法中添加Method Swizzling
方法,我們用來(lái)替換的方法也寫(xiě)在這個(gè)Category中却舀。由于load類(lèi)方法是程序運(yùn)行時(shí)這個(gè)類(lèi)被加載到內(nèi)存中就調(diào)用的一個(gè)方法,執(zhí)行比較早挽拔,并且不需要我們手動(dòng)調(diào)用辆脸。而且這個(gè)方法具有唯一性,也就是只會(huì)被調(diào)用一次螃诅,不用擔(dān)心資源搶奪的問(wèn)題啡氢。
這里我們采用的是第二種方法,正好也驗(yàn)證下Method Swizzling
方法的使用术裸。
2. 方法互換在頁(yè)面統(tǒng)計(jì)上的應(yīng)用實(shí)現(xiàn)
下面我們就直接看代碼吧倘是。
1. JJRuntimeVC.h
#import <UIKit/UIKit.h>
@interface JJRuntimeVC : UIViewController
@end
2. JJRuntimeVC.m
#import "JJRuntimeVC.h"
#import "UIViewController+JJSwizzlingCategory.h"
@interface JJRuntimeVC ()
@end
@implementation JJRuntimeVC
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor greenColor];
}
@end
3. UIViewController+JJSwizzlingCategory.h
#import <UIKit/UIKit.h>
@interface UIViewController (JJSwizzlingCategory)
@end
4. UIViewController+JJSwizzlingCategory.m
#import "UIViewController+JJSwizzlingCategory.h"
#import <objc/runtime.h>
@implementation UIViewController (JJSwizzlingCategory)
#pragma mark - Override Base Function
+ (void)load
{
[super load];
//通過(guò)class_getInstanceMethod()函數(shù)從當(dāng)前對(duì)象中的method list獲取method結(jié)構(gòu)體,如果是類(lèi)方法就使用class_getClassMethod()函數(shù)獲取袭艺。
Method replacedMathod = class_getInstanceMethod([self class], @selector(viewDidLoad));
Method toReplaceMethod = class_getInstanceMethod([self class], @selector(swizzlingMethodViewDidLoad));
// 我們?cè)谶@里使用class_addMethod()函數(shù)對(duì)Method Swizzling做了一層驗(yàn)證搀崭,如果self沒(méi)有實(shí)現(xiàn)被交換的方法,會(huì)導(dǎo)致失敗猾编。
// 而且self沒(méi)有交換的方法實(shí)現(xiàn)瘤睹,但是父類(lèi)有這個(gè)方法,這樣就會(huì)調(diào)用父類(lèi)的方法答倡,結(jié)果就不是我們想要的結(jié)果了轰传。
// 所以我們?cè)谶@里通過(guò)class_addMethod()的驗(yàn)證,如果self實(shí)現(xiàn)了這個(gè)方法瘪撇,class_addMethod()函數(shù)將會(huì)返回NO获茬,我們就可以對(duì)其進(jìn)行交換了港庄。
if (!class_addMethod([self class], @selector(viewDidLoad), method_getImplementation(toReplaceMethod), method_getTypeEncoding(toReplaceMethod))) {
method_exchangeImplementations(replacedMathod, toReplaceMethod);
}
}
#pragma mark - Action && Notification
- (void)swizzlingMethodViewDidLoad
{
NSString *str = [NSString stringWithFormat:@"%@",self.class];
// 我們?cè)谶@里加一個(gè)判斷,將系統(tǒng)的UIViewController的對(duì)象剔除掉
if (![str containsString:@"UI"]) {
NSLog(@"統(tǒng)計(jì)打點(diǎn):%@",self.class);
}
[self swizzlingMethodViewDidLoad];
}
@end
運(yùn)行代碼會(huì)發(fā)現(xiàn)恕曲,先走+ (void)load
實(shí)現(xiàn)方法的互換鹏氧,再走控制器JJRuntimeVC
中的viewDidLoad
方法,但是由于在+ (void)load
中對(duì)方法的實(shí)現(xiàn)做了互換佩谣,所以走的是方法- (void)swizzlingMethodViewDidLoad
度帮,在這個(gè)方法內(nèi)部接著調(diào)用[self swizzlingMethodViewDidLoad];
,同樣由于方法實(shí)現(xiàn)的互換稿存,其實(shí)調(diào)用的是方法- (void)viewDidLoad
,所以最后又走正常的走了這個(gè)方法瞳秽,同時(shí)實(shí)現(xiàn)了對(duì)加載控制器的統(tǒng)計(jì)瓣履,下面看結(jié)果輸出。
2017-07-27 19:07:27.892845+0800 JJOC[5763:1911605] 統(tǒng)計(jì)打點(diǎn):JJRuntimeVC
這樣就利用運(yùn)行時(shí)實(shí)現(xiàn)了頁(yè)面統(tǒng)計(jì)练俐。
參考文獻(xiàn)
1. Method Swizzling
2. Objective-C Runtime 運(yùn)行時(shí)之四:Method Swizzling
3. iOS黑魔法-Method Swizzling
后記
未完袖迎,待續(xù)~~~~