本文為L(zhǎng)_Ares個(gè)人寫作欲逃,以任何形式轉(zhuǎn)載請(qǐng)表明原文出處。
接上一節(jié),繼續(xù)探索录肯。本節(jié)將從
AspectsContainer
蚤假、AspectIdentifier
來(lái)入手栏饮,探索Aspects
庫(kù)到底是如何完成了hook。
一磷仰、先記錄幾個(gè)問題
- 首先袍嬉,已知
Aspects
庫(kù)可以完成在被hook的方法的前、后添加代碼灶平,也可以替換被hook的方法的原有代碼伺通。- 其次,在上一節(jié)的例子中逢享,當(dāng)調(diào)用
Aspects
庫(kù)的公開API中的兩個(gè)方法時(shí)罐监,被hook的方法的_cmd
也就是方法的SEL
名稱發(fā)生了改變,出現(xiàn)了aspects__
前綴瞒爬,變成了aspects__被hook的方法的SEL
弓柱,這是怎么回事?(可見下圖1.1.0
)侧但。- 再次矢空,用來(lái)增加或者替換被hook的方法的
block參數(shù)塊
中的函數(shù),是如何完成了method_swizzling
的禀横。
二屁药、初始化容器AspectsContainer
上一節(jié)探索到 :
AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
這一行代碼的上面,探索完了Aspects
庫(kù)對(duì)被hook的類和被hook的方法的合法性校驗(yàn)柏锄。
這一節(jié)從這一行代碼開始酿箭,先探索aspect_getContainerForObject
和AspectsContainer
复亏。
這里主要是對(duì)
AspectsContainer
做操作。
- 獲取被hook的對(duì)象的
AspectsContainer
容器缭嫡。- 主要思想就是通過關(guān)聯(lián)對(duì)象方式缔御,將被hook的方法
selector
的名字重命名加上前綴變成aspects__selector
,然后以它為鍵械巡,在self
的關(guān)聯(lián)表中查詢對(duì)應(yīng)的容器刹淌。AspectsContainer
只有3個(gè)屬性,都是數(shù)組讥耗,分別存儲(chǔ)beforeAspects
有勾、insteadAspects
、afterAspects
古程。
1. aspect_getContainerForObject
釋義 : 該方法是獲取
AspectsContainer
容器蔼卡。容器對(duì)象存儲(chǔ)的內(nèi)容是所有被hook的對(duì)象/類
。方法的返回值是一個(gè)AspectsContainer
對(duì)象挣磨。
方法的功能和注釋在
下圖2.1.0
中雇逞。
2. aspect_aliasForSelector
釋義 :
上圖2.1.0
中,對(duì)關(guān)聯(lián)表的鍵aliasSelector
的生成方式茁裙。
static SEL aspect_aliasForSelector(SEL selector) {
NSCParameterAssert(selector);
/**
1. 方法中的宏 :
static NSString *const AspectsMessagePrefix = @"aspects_";
2. 返回的值 :
關(guān)聯(lián)表的鍵的命名方式 = "aspects_" + "被hook的方法的SEL"
*/
return NSSelectorFromString([AspectsMessagePrefix stringByAppendingFormat:@"_%@", NSStringFromSelector(selector)]);
}
三塘砸、初始化AspectIdentifier
這里就進(jìn)入到
aspect_add()
函數(shù)對(duì)被hook的類
和被hook的selector
的信息保存。它們的原始信息都存儲(chǔ)在了AspectIdentifier
對(duì)象中晤锥。
//直接調(diào)用的AspectIdentifier的初始化方法掉蔬,構(gòu)造一個(gè)對(duì)象
identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
1. AspectIdentifier的屬性
SEL selector
: 被hook的方法。
id block
: hook后要執(zhí)行的操作女轿。
NSMethodSignature *blockSignature
: block的簽名信息。
id object
: 被hook的類壕翩。
AspectOptions options
:block
的執(zhí)行位置蛉迹。
2. AspectIdentifier初始化方法
很常規(guī)的構(gòu)造函數(shù),唯二的特點(diǎn)是對(duì)參數(shù)中
block塊
的簽名的獲取和校驗(yàn)放妈。
3. aspect_blockMethodSignature
該方法是獲取block的簽名信息北救。
參數(shù)
block
: 要獲取簽名信息的block。error
: 獲取簽名信息出現(xiàn)的錯(cuò)誤信息芜抒。
實(shí)現(xiàn)
上圖3.3.0
中扭倾,AspectBlockRef
結(jié)構(gòu)體的結(jié)構(gòu) :
實(shí)現(xiàn)的思路非常的簡(jiǎn)單,在
block
的章節(jié)中介紹過挽绩,可以進(jìn)我的主頁(yè)看。
實(shí)現(xiàn)思路大體 :
- 通過位移
block
的指針驾中,從block
的首地址唉堪,位移到block
結(jié)構(gòu)體的desc3
上面模聋。desc3
中的signature
元素存儲(chǔ)了block塊
的type encoding
字符。- 利用
NSMethodSignature
的方法唠亚,將type encoding
字符轉(zhuǎn)成NSMethodSignature
對(duì)象链方。這個(gè)對(duì)象也就是所謂的block塊的簽名
。
4. aspect_isCompatibleBlockSignature
該方法是對(duì)上面
3.aspect_blockMethodSignature
獲得的block塊的簽名
的兼容性驗(yàn)證灶搜。
參數(shù)
blockSignature
: 要驗(yàn)證兼容性的block的簽名信息祟蚀。object
: 被hook的類。selector
: 被hook的方法割卖。error
: 錯(cuò)誤信息前酿。
實(shí)現(xiàn)
Aspects
庫(kù)在公開API的注釋中說(shuō)不允許hook
靜態(tài)方法。因?yàn)檫@里調(diào)用的是instanceMethodSignatureForSelector
鹏溯。block塊
的type encoding
格式在上一節(jié)說(shuō)過 :返回值的type encoding
+block的type encoding : @?
+參數(shù)的type encoding
罢维。
- 第一個(gè)位置 : 返回值的
type encoding
。- 第二個(gè)位置 : block的type encoding丙挽,也就是
@?
肺孵。- 判斷一下
block
的參數(shù)數(shù)量是否大于1。
- 等于1說(shuō)明 :
block
的簽名中颜阐,參數(shù)只有block
自己平窘。- 大于1說(shuō)明 :
block
不是一個(gè)空的block
,也就是說(shuō)不是^(void){}
這種無(wú)參數(shù)block
凳怨。而是有除block
自身外瑰艘,其他的參數(shù)。比如上一節(jié)的例子中的 :id<AspectInfo> aspectInfo
(存在的話一定在block
的參數(shù)的第2個(gè)位置上)猿棉、name
磅叛、age
、sex
萨赁。block
的參數(shù)數(shù)量絕對(duì)不可以多于被hook的方法
的參數(shù)數(shù)量弊琴。
四、AspectIdentifier對(duì)象加入AspectsContainer容器
能進(jìn)入到這里杖爽,表明
被hook的類
和被hook的方法
以及block參數(shù)
中用來(lái)插入或者替換的函數(shù)都是符合Aspects
庫(kù)的規(guī)定的敲董。
[aspectContainer addAspect:identifier withOptions:options];
參數(shù)
aspect
: 要加入容器的AspectsIdentifier
對(duì)象。options
:block
的執(zhí)行位置信息慰安。
實(shí)現(xiàn)
- (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)options {
// 1. 斷言區(qū)
NSParameterAssert(aspect);
// 2. 利用positionFilter過濾器腋寨,獲取AspectOptions參數(shù)中的,想要進(jìn)行hook的位置化焕,存入position中
NSUInteger position = options&AspectPositionFilter;
// 3. 利用position判斷被hook的類和方法屬于AspectsContainer容器中哪個(gè)數(shù)組萄窜,存入相應(yīng)的數(shù)組。
switch (position) {
case AspectPositionBefore: self.beforeAspects = [(self.beforeAspects ?:@[]) arrayByAddingObject:aspect]; break;
case AspectPositionInstead: self.insteadAspects = [(self.insteadAspects?:@[]) arrayByAddingObject:aspect]; break;
case AspectPositionAfter: self.afterAspects = [(self.afterAspects ?:@[]) arrayByAddingObject:aspect]; break;
}
}
五、類的準(zhǔn)備工作和方法的Hook
能進(jìn)入這里查刻,也是表明
被hook的類
和被hook的selector
以及用來(lái)插入或置換的block塊函數(shù)
都是符合Aspects
庫(kù)的規(guī)定的键兜。
從這里開始,就是真正的對(duì)
被hook的類
穗泵、被hook的selector
普气、block塊內(nèi)的函數(shù)
進(jìn)行操作了。
aspect_prepareClassAndHookSelector(self, selector, error);
1. 方法的整體邏輯
先來(lái)看這個(gè)函數(shù)的整體實(shí)現(xiàn)邏輯佃延,然后挑出其中的封裝邏輯再詳細(xì)探索现诀。
參數(shù)
self
: 被hook的對(duì)象。
selector
: 被hook的方法履肃。
error
: 可能發(fā)生的錯(cuò)誤信息仔沿。
實(shí)現(xiàn)
2. aspect_hookClass()
該方法是對(duì)
被hook的對(duì)象
的類做hook后的處理。
我給這個(gè)方法分成了3個(gè)區(qū)域榆浓,方便理解于未,最主要的是看
圖5.2.0
中的注釋,下面依次說(shuō)明3個(gè)區(qū)域的功能陡鹃。
2.1 方法準(zhǔn)備區(qū)
代碼非常的簡(jiǎn)單烘浦,邏輯也簡(jiǎn)單 :
- 斷言判斷參數(shù)的合法性。
statedClass
:
- 如果
self
是類對(duì)象 :statedClass
就是類對(duì)象本身萍鲸。- 如果
self
是實(shí)例對(duì)象 :statedClass
就是實(shí)例對(duì)象所屬的類闷叉。baseClass
:
- 如果
self
是類對(duì)象 :baseClass
就是元類。- 如果
self
是實(shí)例對(duì)象 :baseClass
就是實(shí)例對(duì)象所屬的類脊阴。className
:
- 如果
self
是類對(duì)象 :className
就是元類名稱字符串握侧。- 如果
self
是實(shí)例對(duì)象 :className
就是實(shí)例對(duì)象所屬的類的名稱字符串。
2.2 特殊情況區(qū)
if ([className hasSuffix:AspectsSubclassSuffix])
:如果
self
的isa指向的類
已經(jīng)有_Aspects_
后綴嘿期。例如上節(jié)案例中的ViewController
品擎,原本它的isa指向的類
是元類ViewController
,如果它的isa指向的類
變成了ViewController_Aspects_
备徐,則表明它被hook過萄传。可以直接返回
baseClass
。
else if (class_isMetaClass(baseClass))
:進(jìn)入這里蜜猾,則代表在調(diào)用
Aspects
的公開API
時(shí)秀菱,調(diào)用的是+
方法,也就是類方法蹭睡,說(shuō)明要hook的是整個(gè)類對(duì)象衍菱,而不是類的某個(gè)實(shí)例對(duì)象。返回
aspect_swizzleClassInPlace((Class)self)
肩豁。
else if (statedClass != baseClass)
:進(jìn)入這里脊串,表示上一步?jīng)]有發(fā)生辫呻,也就是說(shuō),實(shí)例對(duì)象才會(huì)進(jìn)入到這個(gè)判斷洪规。
一般情況下印屁,
實(shí)例對(duì)象的類
和實(shí)例對(duì)象的isa指向的類
是同一個(gè)類。
如果發(fā)生不是同一個(gè)類的情況斩例,則證明,該對(duì)象有可能出現(xiàn)了特殊情況从橘,比如進(jìn)行著鍵值觀察(KVO
)念赶。返回
aspect_swizzleClassInPlace(baseClass)
。
在這個(gè)區(qū)域恰力,處理了公開API
中叉谜,類方法的調(diào)用者。以及正在被KVO
觀察的對(duì)象的isa指向的類
踩萎。并且停局,它們調(diào)用的方法都是aspect_swizzleClassInPlace
,只不過傳參不同香府。
2.2.1 aspect_swizzleClassInPlace
static Class aspect_swizzleClassInPlace(Class klass) {
//斷言區(qū)
NSCParameterAssert(klass);
//獲取傳入類的名稱字符串
NSString *className = NSStringFromClass(klass);
//單例創(chuàng)建的一個(gè)集合董栽,存儲(chǔ)已經(jīng)發(fā)生swizzled的類
//函數(shù)的參數(shù)是一個(gè)block,那么參數(shù)block的執(zhí)行企孩,就要看函數(shù)的實(shí)現(xiàn)中锭碳,block在哪里被執(zhí)行
//所以block里面的代碼,需要看函數(shù)的實(shí)現(xiàn)勿璃,才能知道什么時(shí)候被執(zhí)行
_aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {
//如果當(dāng)前被hook的類擒抛,不在已發(fā)生swizzled的類集合中
if (![swizzledClasses containsObject:className]) {
//swizzled被hook的類的forwardInvocation方法
aspect_swizzleForwardInvocation(klass);
//添加這個(gè)類到已發(fā)生swizzled的類的集合
[swizzledClasses addObject:className];
}
});
return klass;
}
有兩點(diǎn) :
- 是
_aspect_modifySwizzledClasses
- 是
aspect_swizzleForwardInvocation
先看_aspect_modifySwizzledClasses
。
2.2.2 _aspect_modifySwizzledClasses
static void _aspect_modifySwizzledClasses(void (^block)(NSMutableSet *swizzledClasses)) {
//定義靜態(tài)可變集合补疑,存儲(chǔ)已經(jīng)發(fā)生過混合的類
static NSMutableSet *swizzledClasses;
//下面很明顯是單例模式創(chuàng)建可變集合
static dispatch_once_t pred;
dispatch_once(&pred, ^{
swizzledClasses = [NSMutableSet new];
});
//這里就是block被執(zhí)行的地方歧沪,用自旋鎖保證線程安全
@synchronized(swizzledClasses) {
block(swizzledClasses);
}
}
明顯是一個(gè)以
帶參數(shù)的block
做參數(shù)的函數(shù)。先初始化一個(gè)靜態(tài)的可變集合莲组,用來(lái)存儲(chǔ)已經(jīng)發(fā)生過
swizzled
的類诊胞,利用單例初始化,然后把集合當(dāng)參數(shù)胁编,傳入?yún)?shù)block
厢钧,并且調(diào)用block
。所以嬉橙,最后的重點(diǎn)還是
block
內(nèi)都對(duì)傳入的類做了什么早直。
再看aspect_swizzleForwardInvocation
2.2.3 aspect_swizzleForwardInvocation
static void aspect_swizzleForwardInvocation(Class klass) {
//斷言區(qū)
NSCParameterAssert(klass);
// If there is no method, replace will act like class_addMethod.
/**
1. class_replaceMethod : 可以看一下蘋果的官方文檔,如果方法不存在市框,這個(gè)方法會(huì)像class_addMethod一樣去添加這個(gè)方法到klass里面霞扬。
2. 替換klass(被hook的)的forwardInvocation方法的IMP實(shí)現(xiàn),并且把原有的IMP返回。
3. 之所以要操作klass的forwardInvocation方法喻圃,是因?yàn)榉椒ǖ淖詈蟛檎也襟E是forwardInvocation:消息轉(zhuǎn)發(fā)
*/
IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@");
//如果被hook的對(duì)象的isa指向的中間類(也就是添加了_Aspects_后綴的中間類)萤彩,已經(jīng)實(shí)現(xiàn)了forwardInvocation方法
if (originalImplementation) {
//將原有就存在的forwardInvocation方法的IMP添加給__aspects_forwardInvocation:這個(gè)方法
class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");
}
//打印日志
AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
}
也就是說(shuō),這個(gè)方法把傳入類的
forwardInvocation:
方法的IMP替換了斧拍。對(duì)于參數(shù)
klass
:
如果
klass
是類本身雀扶,也就是通過+
方法進(jìn)入到這里,那么整個(gè)類的forwardInvocation
方法的IMP
實(shí)現(xiàn)肆汹,都將被替換成__ASPECTS_ARE_BEING_CALLED__
愚墓。(關(guān)于其他未被hook的方法如果沒有實(shí)現(xiàn),該怎么辦昂勉,后面在該函數(shù)的解釋里會(huì)說(shuō)明)如果
klass
是另一種情況浪册,即被hook的對(duì)象
的isa指向的類
不是一般情況下的自己的父類,而是出現(xiàn)類似KVO鍵值觀察
的情況岗照。那么要替換的就是NSKVONotifying_父類名
類的forwardInvocation
方法的IMP
實(shí)現(xiàn)村象。問題 :
其實(shí)這里會(huì)出現(xiàn)一種情況 :
當(dāng)
被hook的對(duì)象
是一個(gè)KVO
鍵值觀察對(duì)象或者就是普通的實(shí)例對(duì)象,如果被hook的對(duì)象
所屬的類中有未被實(shí)現(xiàn)的方法攒至,并且厚者,沒實(shí)現(xiàn)的方法還不是被hook的方法
。那么首先就會(huì)進(jìn)入
動(dòng)態(tài)決議
嗓袱,如果動(dòng)態(tài)決議
依舊沒有實(shí)現(xiàn)籍救,則會(huì)進(jìn)入_objec_msgForward
,進(jìn)行消息轉(zhuǎn)發(fā)渠抹。如果我們不實(shí)現(xiàn)消息轉(zhuǎn)發(fā)流程中的
快速轉(zhuǎn)發(fā)
蝙昙,則最后會(huì)調(diào)用一次NSKVONotifying_父類名
類的forwardInvocation
方法。但是這個(gè)方法的IMP被交換了梧却,那是不是沒實(shí)現(xiàn)的方法也會(huì)進(jìn)入
__ASPECTS_ARE_BEING_CALLED__
這個(gè)IMP實(shí)現(xiàn)呢奇颠?會(huì)怎么處理呢?在下面__ASPECTS_ARE_BEING_CALLED__
方法解析的時(shí)候會(huì)有介紹放航。
2.3 默認(rèn)情況區(qū)
這里就是
aspect_hookClass()
函數(shù)的默認(rèn)實(shí)現(xiàn)烈拒,也就是針對(duì) :
- 從未經(jīng)過
aspect_hook()
處理的實(shí)例對(duì)象。- 不是類對(duì)象广鳍。
- 不是
KVO
觀察對(duì)象荆几。這三種情況除外的,普通的赊时,被hook的實(shí)例對(duì)象的吨铸,類的處理。
// Default case. Create dynamic subclass.
//默認(rèn)的情況下祖秒,上面的幾個(gè)if條件都不滿足诞吱,那么就要自己動(dòng)手創(chuàng)建動(dòng)態(tài)的子類
//給isa所指的類的名字前面加上Aspects庫(kù)的后綴
const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
//看清楚這里舟奠,這是objc_getClass而不是object_getClass
//兩者有明確的區(qū)別,objc_getClass(subclassName)和[self class]有點(diǎn)像房维,返回的都是類本身
//只不過objec_getClass的參數(shù)是const char*類型沼瘫,傳入類的名字就可以拿到一個(gè)類
//而object_getClass則是獲取參數(shù)的isa指向的類
Class subclass = objc_getClass(subclassName);
//如果這個(gè)類還不存在
if (subclass == nil) {
//創(chuàng)建subclass的元類和類,并設(shè)置baseClass為subclass的父類
subclass = objc_allocateClassPair(baseClass, subclassName, 0);
//這個(gè)subclass不能被創(chuàng)建
if (subclass == nil) {
//報(bào)錯(cuò)
NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName];
AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc);
//返回nil
return nil;
}
//設(shè)置這個(gè)新類的forwardInvocation方法的IMP
aspect_swizzleForwardInvocation(subclass);
//設(shè)置新類的-(void)class方法的IMP咙俩,讓新類的-(void)class方法返回的是被hook的對(duì)象的類
aspect_hookedGetClass(subclass, statedClass);
//設(shè)置新類的元類的-(void)class方法(也就是新類的+(void)class方法)的IMP耿戚,也是返回被hook的對(duì)象的類
aspect_hookedGetClass(object_getClass(subclass), statedClass);
//把新類注冊(cè)到runtime中,這樣這個(gè)新類才算realized的暴浦。
objc_registerClassPair(subclass);
}
//設(shè)置self(被hook的對(duì)象)的isa指向?yàn)閟ubclass類(新類)
object_setClass(self, subclass);
//返回這個(gè)新類
return subclass;
還是重點(diǎn)看一下注釋情況溅话,然后從中可以找到一個(gè)沒有解析過,并且封裝起來(lái)的方法
aspect_hookedGetClass
歌焦。看實(shí)現(xiàn)砚哆。
static void aspect_hookedGetClass(Class class, Class statedClass) {
//class是新類和新類的元類独撇,有Aspects庫(kù)的后綴。
//statedClass是被hook的對(duì)象的類
//斷言區(qū)
NSCParameterAssert(class);
NSCParameterAssert(statedClass);
//拿到class類的-(void)class方法
Method method = class_getInstanceMethod(class, @selector(class));
//設(shè)置一個(gè)新的IMP躁锁,IMP的實(shí)現(xiàn)是返回一個(gè)被hook的對(duì)象的類
IMP newIMP = imp_implementationWithBlock(^(id self) {
return statedClass;
});
//替換掉class類的-(void)class方法的IMP為newIMP纷铣,返回statedClass類
class_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method));
}
這個(gè)方法的目的 :
- 把新生成的
中間類
和中間類的元類
的class
方法,全都返回被hook的對(duì)象的類
战转。- 這樣做了以后搜立,哪怕在下面的
object_setClass
中,將被hook的對(duì)象
的isa指向
變成了中間類
槐秧,也不會(huì)影響被hook的對(duì)象
調(diào)用-(void)class
方法返回的是其原來(lái)的類啄踊。
3. 被hook的方法的處理
上面的2.aspect_hookClass()完成了對(duì)
被hook的對(duì)象
的類的處理,這里則開始對(duì)被hook的方法
進(jìn)行處理刁标。
這里截取的是上圖5.1.0
中颠通,Class klass = aspect_hookClass(self,error)
之后的代碼,也就是對(duì)被hook的方法
的處理膀懈。
看圖中畫了紅框的部分顿锰,從上到下,依此說(shuō)一下設(shè)計(jì)實(shí)現(xiàn)的思路启搂。
1. 首先硼控,Aspects
庫(kù)的作者是利用class_getInstanceMethod
來(lái)獲取klass
中的SEL
為selector
的方法的。
Method targetMethod = class_getInstanceMethod(klass, selector);
使用
class_getInstanceMethod
是因?yàn)?code>klass已經(jīng)被處理過胳赌,被處理的klass
無(wú)非就3種情況牢撼,在上面的aspect_hookClass
中已經(jīng)介紹過,被hook的對(duì)象
可能是 :
是普通的實(shí)例對(duì)象 :
則klass
就是中間類
匈织。
中間類
名稱是組成是 :被hook的對(duì)象
的類的名字 +_Aspects_
后綴浪默。是類對(duì)象 :
則klass
的類不發(fā)生改變牡直,依然是被hook的類對(duì)象
。是被
KVO
鍵值觀察的實(shí)例對(duì)象 :
則klass
是KVO
的中間類纳决。
KVO
的中間類名稱組成是 :NSKVONotifying_
+實(shí)例對(duì)象的類名
這3種
klass
都有著一個(gè)絕對(duì)的共同點(diǎn) : 全部繼承于被hook的對(duì)象
的類碰逸。所以,
class_getInstanceMethod
一定可以在klass
的繼承鏈上阔加,找到selector
的方法饵史。然后得到targetMethod
。
2. 獲得targetMethod
的IMP
沒什么可說(shuō)的胜榔,就是獲得
被hook的方法
的原始IMP
胳喷,直接用objc
的API
,獲得Method
的IMP
夭织。
IMP targetMethodIMP = method_getImplementation(targetMethod);
3. 判斷被hook的方法
的原始IMP
如果不是直接調(diào)用_objc_msgForward
if (!aspect_isMsgForwardIMP(targetMethodIMP))
aspect_isMsgForwardIMP
的實(shí)現(xiàn) :
static BOOL aspect_isMsgForwardIMP(IMP impl) {
//這個(gè)是arm64架構(gòu)吭露,也就是iOS系統(tǒng),手機(jī)真機(jī)的情況下尊惰,會(huì)直接調(diào)用_objc_msgForward
return impl == _objc_msgForward
//不要看這里了讲竿,這是非arm64架構(gòu)下的,消息轉(zhuǎn)發(fā)是調(diào)用的_objc_msgForward_stret
#if !defined(__arm64__)
|| impl == (IMP)_objc_msgForward_stret
#endif
;
}
一般正常的情況下弄屡,我們是不會(huì)直接給一個(gè)方法的實(shí)現(xiàn)寫成
_objc_msgForward
的题禀。所以大多數(shù)的情況,這里的BOOL
值都是NO
膀捷。一般都會(huì)進(jìn)入if判斷
中的代碼迈嘹。
4. 給中間類添加一個(gè)方法
// Make a method alias for the existing method implementation, it not already copied.
//獲取被hook的方法的typeEncoding
const char *typeEncoding = method_getTypeEncoding(targetMethod);
//獲取被hook的方法的別名SEL
SEL aliasSelector = aspect_aliasForSelector(selector);
//如果被hook的類不響應(yīng)這個(gè)aliasSelector
if (![klass instancesRespondToSelector:aliasSelector]) {
//把這個(gè)aliasSelector關(guān)聯(lián)上targetMethod的實(shí)現(xiàn),然后添加到klass上
__unused BOOL addedAlias = class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding);
NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);
}
也很好理解全庸,只是給klass
添加了一個(gè)新的方法秀仲,方法的SEL
名稱格式是 :
aspects_
+ 被hook的方法的SEL
。很熟悉糕篇,在給容器添加關(guān)聯(lián)對(duì)象的時(shí)候出現(xiàn)過啄育,這個(gè)SEL
名稱是容器在關(guān)聯(lián)表中的鍵。
5. 替換被hook的方法
的IMP
//我們利用forwardInvocation方法hook進(jìn)去
//用_objc_msgForward(消息轉(zhuǎn)發(fā))替換被hook的方法的IMP
class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
aspect_getMsgForwardIMP
的實(shí)現(xiàn)拌消,我只截取arm64架構(gòu)
下的實(shí)現(xiàn) :
static IMP aspect_getMsgForwardIMP(NSObject *self, SEL selector) {
//只看這里挑豌,這里是arm64架構(gòu)
IMP msgForwardIMP = _objc_msgForward;
//不用看了
#if !defined(__arm64__)
... ...
#endif
return msgForwardIMP;
}
所以,現(xiàn)在墩崩,
被hook的方法
的SEL
還是原來(lái)的SEL
氓英,但是IMP
已經(jīng)換成了objc_msgForward
了。
也就是說(shuō)鹦筹,現(xiàn)在如果再調(diào)用
被hook的方法
铝阐,就相當(dāng)于直接調(diào)用objc_msgForward
,進(jìn)入消息轉(zhuǎn)發(fā)铐拐。而上面我們剛說(shuō)過徘键,
klass
類的forwardInvocation
全部都被替換成了__ASPECTS_ARE_BEING_CALLED__
练对,也就是說(shuō) :當(dāng)完成
aspect_prepareClassAndHookSelector
后,再調(diào)用被hook的方法
吹害,相當(dāng)于直接調(diào)用到了__ASPECTS_ARE_BEING_CALLED__
螟凭。但是這里還有一個(gè)問題存在,如果我實(shí)現(xiàn)了
forwardingTargetForSelector
怎么辦?這個(gè)問題放到最后一起解決它呀。
注釋
如何執(zhí)行被替換的block和如何執(zhí)行原有方法螺男,將會(huì)放入下一節(jié),AOP之Aspects庫(kù)(三)進(jìn)行探索纵穿。