Runtime窺探 (六)| AOP與Aspects核心源碼

前言

如何把這個(gè)世界變得美好?把你自己變得更美好

秋天來(lái)了

我們這篇博客繼續(xù)來(lái)介紹Runtime在開(kāi)發(fā)中的實(shí)際應(yīng)用,通過(guò)開(kāi)源庫(kù)Aspects來(lái)對(duì)runtime有更好的認(rèn)識(shí)和理解。

一、Aspects庫(kù)

這個(gè)庫(kù)是iOS基于AOP編程思想的開(kāi)源庫(kù),用于跟蹤修改一個(gè)指定的類的某個(gè)方法執(zhí)行前/替換/后,同時(shí)可以自定義添加一段代碼塊.對(duì)這個(gè)類的所有對(duì)象都會(huì)起作用。

所有的調(diào)用都會(huì)是線程安全的.Aspects 使用了Runtime的消息轉(zhuǎn)發(fā)機(jī)制以及method swizzling,會(huì)有一定的性能消耗.所有對(duì)于過(guò)于頻繁的調(diào)用,不建議使用 Aspects.Aspects更適用于視圖/控制器相關(guān)的等。并不適用于每秒調(diào)用不超過(guò)1000次的代碼.

二、AOP

基本概念:

  • AOP(Aspect Oriented Programming)是面向切面編程。通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)的編程思想。

  • 利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開(kāi)發(fā)的效率。

主要功能:

  • 日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理,事務(wù)的處理等等。

主要意圖:

  • 將日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理等代碼從業(yè)務(wù)邏輯代碼中劃分出來(lái),通過(guò)對(duì)這些行為的分離,我們希望可以將它們獨(dú)立到非指導(dǎo)業(yè)務(wù)邏輯的方法中,進(jìn)而改變這些行為的時(shí)候不影響業(yè)務(wù)邏輯的代碼。

說(shuō)明:

  • 我們?cè)陂_(kāi)發(fā)一個(gè)應(yīng)用程序的時(shí)候,把系統(tǒng)分為很多模塊(如:首頁(yè)、分類、購(gòu)物車、我的等模塊)。我們從立體上看就是一個(gè)并列的樹(shù)形結(jié)構(gòu),屬于縱向切入系統(tǒng)。也就是OOP的的編程思想(面向?qū)ο缶幊趟枷耄鳤OP就屬于橫向切入系統(tǒng),把整個(gè)系統(tǒng)的重復(fù)操的的部分提取出來(lái)(Log打印、日志記錄、應(yīng)用系統(tǒng)的異常捕捉及處理等等),由此可見(jiàn),AOP是OOP的一個(gè)有效補(bǔ)充。

  • 假設(shè)把應(yīng)用程序想成一個(gè)立體結(jié)構(gòu)的話,OOP的利刃是縱向切入系統(tǒng),把系統(tǒng)劃分為很多個(gè)模塊(如:用戶模塊,文章模塊等等),而AOP的利刃是橫向切入系統(tǒng),提取各個(gè)模塊可能都要重復(fù)操作的部分(如:權(quán)限檢查,日志記錄等等)。

注意:

  • AOP不是一種具體代碼實(shí)現(xiàn)的技術(shù),實(shí)際上是編程思想。凡是符合AOP思想的技術(shù)編程,都可以看成是AOP的實(shí)現(xiàn)。

三、解析Aspects庫(kù)

我們經(jīng)常用的Method Swizzling就是一種AOP思想實(shí)現(xiàn),Aspects是比較很棒的基于AOP編程思想的開(kāi)源庫(kù),
由于Aspects的代碼較多,我們只是來(lái)閱讀Aspects的核心實(shí)現(xiàn)思路和流程。

1.Aspects的基本模塊

  • AspectInfo

    • Aspect信息類:用來(lái)保存信息的,存放被hook的實(shí)例、方法、參數(shù)等
  • AspectIdentifier

    • Aspect標(biāo)識(shí)類:用來(lái)追蹤一個(gè)唯一的aspect,AspectIdentifier對(duì)應(yīng)的實(shí)例,里面會(huì)包含了單個(gè)的 Aspect 的具體信息,包括執(zhí)行時(shí)機(jī),要執(zhí)行 block 所需要用到的具體信息:包括方法簽名、參數(shù)等等。初始化AspectIdentifier的過(guò)程實(shí)質(zhì)是把我們傳入的block打包成AspectIdentifier。
  • AspectsContainer

    • 是一個(gè)用來(lái)存儲(chǔ)所有的aspect的容器,可能存儲(chǔ)實(shí)例方法/類方法。所以會(huì)有兩種容器
  • AspectTracker

    • AspectTracker來(lái)跟蹤要被hook的類

上面這些模塊都是用來(lái)輔助核心思想實(shí)現(xiàn)的,使開(kāi)源庫(kù)模塊清晰、較高容錯(cuò)率、職責(zé)明確等等,這些模塊還是比較好理解的,就不一一閱讀了。其實(shí)很多優(yōu)秀開(kāi)源庫(kù)都會(huì)有類似的模塊(比如信息、容器、唯一標(biāo)識(shí)等等)。下面我們主要了解Aspects的核心思想以及流程。

2.小插曲

為什么大多數(shù)開(kāi)源庫(kù)都會(huì)有這些模塊?舉個(gè)例子:

和女朋友一起去溜一堆狗......

  • 我們?cè)趺磥?lái)控制一堆狗不亂跑呢?那就用繩子把狗拴起來(lái),把繩子握在手里或者你想綁在腿上,這時(shí)候手或腿就是來(lái)控制所有狗的工具,也就是我們所說(shuō)的容器(用來(lái)保存所有的個(gè)體信息)。

  • 我們?cè)趺磥?lái)看這個(gè)狗的名字、品種、年齡?我們可以制作一個(gè)標(biāo)簽掛在狗脖子上,上面寫著狗的基本信息。也就是我們所說(shuō)的信息模塊(用來(lái)存儲(chǔ)個(gè)體基本信息)。

  • 現(xiàn)在女朋友要溜xx這條狗,怎么給她?就需要一個(gè)唯一標(biāo)識(shí)狗個(gè)體的工具,那我們可以在繩子上有個(gè)編號(hào)便簽,也就是我們所說(shuō)的唯一標(biāo)識(shí)模塊(標(biāo)識(shí)某個(gè)個(gè)體,包含基本信息以及其他輔助信息)。這樣我們可以通過(guò)編號(hào)就可以找到對(duì)應(yīng)的狗,而且不會(huì)找錯(cuò),這樣就可以愉快的來(lái)遛狗。

  • 上面這些工作都是來(lái)輔助我們遛狗(核心模塊)

3.Aspects對(duì)外接口以及基本說(shuō)明

通過(guò)源代碼Aspects中可以看到下面兩個(gè)對(duì)外公開(kāi)接口用于hook selector

@interface NSObject (Aspects)

/***********************
第一個(gè)參數(shù)selector:是要給它增加切面的原方法
第二個(gè)參數(shù)是AspectOptions:是代表這個(gè)切片增加在原方法的before / instead / after
第三個(gè)入?yún)lock:這個(gè)block復(fù)制了正在被hook的方法的簽名signature類型
第一個(gè)參數(shù)selector將返回一個(gè)遵循<AspectInfo>的id對(duì)象,這個(gè)對(duì)象繼承了方法的所有參數(shù),
這些參數(shù)都會(huì)被填充到匹配的block的簽名里
你也可以使用一個(gè)空block,或者一個(gè)簡(jiǎn)單的id<AspectInfo>
不支持hook靜態(tài)static方法的
返回一個(gè)可以用來(lái)撤銷aspect的token
***********************/

//hook類方法,hook一個(gè)類的所有實(shí)例對(duì)應(yīng)的一個(gè)方法
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
                           withOptions:(AspectOptions)options
                            usingBlock:(id)block
                                 error:(NSError **)error;
//hook實(shí)例方法,hook類的一個(gè)具體實(shí)例對(duì)應(yīng)的一個(gè)方法
//為一個(gè)具體實(shí)例的seletor的執(zhí)行 之前/或者被替換/之后 添加一個(gè)block代碼
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
                           withOptions:(AspectOptions)options
                            usingBlock:(id)block
                                 error:(NSError **)error;

@end

方法說(shuō)明:

  • 第一個(gè)方法為類方法:也就是說(shuō)接受者是一個(gè)要被hook的類,也就是說(shuō)hook一個(gè)類的所有實(shí)例對(duì)應(yīng)的一個(gè)方法。會(huì)對(duì)類進(jìn)行消息轉(zhuǎn)發(fā)和method swizzling。會(huì)對(duì)類中methodLists的兩個(gè)方法進(jìn)行修改:

    • 1.forwardInvocation:
      forwardInvocation:的IMP(方法實(shí)現(xiàn))被替換為:__ASPECTS_ARE_BEING_CALLED__,這個(gè)函數(shù)內(nèi)部具體執(zhí)行被hook的selector和切入操作的實(shí)現(xiàn)。forwardInvocation: 的本來(lái)的IMP被保存在__aspects_forwardInvocation:中。在調(diào)用aspect_hookClass()函數(shù)會(huì)進(jìn)行forwardInvocation:的替換

    • 2.要被hook的selector:要被hook的selector的IMP被替換為:_objc_msgForward/_objc_msgForward_stret,這個(gè)函數(shù)用于直接觸發(fā)消息轉(zhuǎn)發(fā)機(jī)制,而不會(huì)在methodLists查找函數(shù)來(lái)執(zhí)行。被hook的selector的原始IMP被保存在方法aspects_selector中。在調(diào)用aspect_prepareClassAndHookSelector()函數(shù)會(huì)進(jìn)行selector的替換

    • 3.當(dāng)我們調(diào)用[objc message];時(shí)就直接觸發(fā)消息轉(zhuǎn)發(fā)機(jī)制調(diào)用forwardInvocation:方法,而實(shí)現(xiàn)就是__ASPECTS_ARE_BEING_CALLED__這個(gè)函數(shù)(真正執(zhí)行的函數(shù)實(shí)體),從而實(shí)現(xiàn)selector的hook。

  • 第二個(gè)方法為實(shí)例方法:也就是說(shuō)接受者是一個(gè)要被hook的類的實(shí)例對(duì)象,也就是說(shuō)hook類的一個(gè)具體實(shí)例對(duì)應(yīng)的一個(gè)方法。這里跟上面區(qū)別主要是新建子類用來(lái)操作hook,具體步驟如下:
    • 1.新建一個(gè)要被hook的類的子類:xxx__Aspects_
    • 2.把要hook的實(shí)例的isa指向上面新生成的子類xxx__Aspects_,也就是說(shuō)當(dāng)前這個(gè)實(shí)例變成了子類xxx__Aspects_的實(shí)例對(duì)象。
    • 3.對(duì)上面新建子類xxx__Aspects_進(jìn)行消息轉(zhuǎn)發(fā)和method swizzling。具體思路就是和上面的類方法流程一樣。

上面是Aspects的核心思想以及流程的簡(jiǎn)單說(shuō)明,下面我們對(duì)這些核心代碼進(jìn)行梳理介紹。

注意:

  • Aspects并不能hook類的類方法
  • Aspects不能hook靜態(tài)方法
  • Aspects不能hook類中不存在或者未實(shí)現(xiàn)的方法

4.Aspects核心代碼解析

對(duì)外接口源碼

1.上圖說(shuō)明:

  • 不管調(diào)用hook類的的實(shí)例方法還是類方法,在函數(shù)內(nèi)都會(huì)調(diào)起私有c函數(shù)static id aspect_add()來(lái)進(jìn)行統(tǒng)一處理
  • 使用自旋鎖來(lái)保證線程安全執(zhí)行
  • 然后會(huì)進(jìn)行前期準(zhǔn)備工作處理(如黑名單排除、生成標(biāo)識(shí)實(shí)例以及添加、block塊的函數(shù)簽名和轉(zhuǎn)換處理等等),這些通過(guò)源碼還是比較好理解的
  • 把前期準(zhǔn)備工作做完后,就會(huì)調(diào)用函數(shù)aspect_prepareClassAndHookSelector(self, selector, error);來(lái)進(jìn)行核心模塊實(shí)現(xiàn)

下面我們對(duì)函數(shù)aspect_prepareClassAndHookSelector(self, selector, error);來(lái)進(jìn)行查看源碼

2.核心函數(shù)aspect_prepareClassAndHookSelector()

函數(shù)aspect_prepareClassAndHookSelector()的具體實(shí)現(xiàn)如下:

aspect_prepareClassAndHookSelector
  • 函數(shù)主要分類兩部分處理:
    • 一個(gè)是hook Class的處理,這一部分主要實(shí)現(xiàn)上面提到的forwardInvocation:函數(shù)的替換具體過(guò)程
    • 一個(gè)是hook selector的處理,這一部分主要實(shí)現(xiàn)上面提到的要把被hook的selector的函數(shù)實(shí)現(xiàn)替換成_objc_msgForward/_objc_msgForward_stret,直接觸發(fā)消息轉(zhuǎn)發(fā)機(jī)制調(diào)用forwardInvocation:

3.hook Class過(guò)程

我們先來(lái)看一下核心函數(shù)aspect_prepareClassAndHookSelector()中的第一部分hook Class過(guò)程,這個(gè)會(huì)調(diào)用aspect_hookClass函數(shù)

aspect_hookClass
  • 上面根據(jù)self獲取類對(duì)象/元類的Class

  • 先對(duì)當(dāng)前類進(jìn)行篩選,如果當(dāng)前Class是以xxx_Aspects_后綴結(jié)尾的名稱,說(shuō)明這個(gè)Class已經(jīng)被hook過(guò)了,不需要再進(jìn)行下面重復(fù)處理,直接返回當(dāng)前Class,去執(zhí)行hook selector的過(guò)程(后面會(huì)說(shuō))

  • 然后再看baseClass是不是元類,object_getClass(self)獲取self的isa指針。如果當(dāng)前是類對(duì)象,則class_isMetaClass(baseClass)是元類,說(shuō)明當(dāng)前hook的是某一個(gè)類的所有實(shí)例的對(duì)應(yīng)方法。直接調(diào)用函數(shù)aspect_swizzleClassInPlace()(后面介紹)來(lái)method swizzling函數(shù)forwardInvocation:

  • 判斷當(dāng)前實(shí)例對(duì)象的isa指向statedClass和baseClass,按理說(shuō)當(dāng)self為實(shí)例變量時(shí),object_getClass(self)與[self class]輸出結(jié)果一直,均獲得isa指針,即指向類對(duì)象的指針。但是這里判斷相不相等?我們上一篇博客說(shuō)過(guò)KVO過(guò)的對(duì)象的isa會(huì)指向一個(gè)中間類NSKVONotifying_XXX,所以說(shuō)不相等時(shí),說(shuō)明這個(gè)實(shí)例對(duì)象是被KVO觀察的對(duì)象。直接調(diào)用函數(shù)aspect_swizzleClassInPlace()來(lái)method swizzling函數(shù)forwardInvocation:

  • 上面情況都排除了,說(shuō)明hook的是某一個(gè)類的實(shí)例的對(duì)應(yīng)方法,下面就是hook類方法和實(shí)例方法的區(qū)別了

    • 1.新建當(dāng)前self的所屬類的子類:xxx__Aspects_
    • 2.調(diào)用aspect_swizzleForwardInvocation()替換函數(shù)forwardInvocation:的實(shí)現(xiàn)
    • 3.調(diào)用函數(shù)aspect_hookedGetClass(),把新建子類xxx__Aspects_的isa指向self的所屬類,把新建子類xxx__Aspects_的元類的isa指向self的所屬類
    • 4.上面完成后,注冊(cè)新類說(shuō)明新建子類創(chuàng)建完畢
    • 5.把當(dāng)前self的isa指向新建子類xxx__Aspects_,成功的把self hook成了其子類 xxx_Aspects_,也就是self所屬類是xxx_Aspects_,而不再是原始類xxx了。

上面就是整個(gè)hook Class的過(guò)程,流程圖如下:

hook Class

上面都會(huì)調(diào)用有一個(gè)函數(shù)aspect_swizzleClassInPlace,這個(gè)函數(shù)的作用就是我們來(lái)替換Class系統(tǒng)方法forwardInvocation:的實(shí)現(xiàn),源代碼如下

//替換類的快速消息轉(zhuǎn)發(fā)方法,并把類添加到交換類的集合中
static Class aspect_swizzleClassInPlace(Class klass) {
    NSCParameterAssert(klass);
    NSString *className = NSStringFromClass(klass);

    _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {
        if (![swizzledClasses containsObject:className]) {
            //不包含,就調(diào)用aspect_swizzleForwardInvocation()方法,并把className加入到Set集合里面。
            aspect_swizzleForwardInvocation(klass);
            [swizzledClasses addObject:className];
        }
    });
    return klass;
}

//類的forwardInvocation方法替換為_(kāi)_ASPECTS_ARE_BEING_CALLED__的實(shí)現(xiàn),返回新函數(shù)imp
static NSString *const AspectsForwardInvocationSelectorName = @"__aspects_forwardInvocation:";
static void aspect_swizzleForwardInvocation(Class klass) {
    NSCParameterAssert(klass);

    //替換類中已有方法的實(shí)現(xiàn),返回原來(lái)函數(shù)imp
    IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@");
    
    if (originalImplementation) {
        //originalImplementation不為空的話說(shuō)明原方法有實(shí)現(xiàn),添加一個(gè)新方法保存原來(lái)類的ForwardInvocation方法實(shí)現(xiàn)
        class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");
    }
    AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
}
  • 從上面代碼中看出,把傳入的Class加入到集合中用于hook完成后若需移除時(shí)用到,同時(shí)調(diào)用函數(shù)aspect_swizzleForwardInvocation()
  • 函數(shù)aspect_swizzleForwardInvocation()中可以看到:使用class_replaceMethod方法把Class的forwardInvocation:函數(shù)實(shí)現(xiàn)替換成了函數(shù)__ASPECTS_ARE_BEING_CALLED__,這個(gè)才是真正的函數(shù)執(zhí)行入口。同時(shí)把類的原始forwardInvocation:函數(shù)實(shí)現(xiàn)保存在了__aspects_forwardInvocation:,用于后面hook selector不成功時(shí),調(diào)用原始的forwardInvocation:函數(shù)來(lái)執(zhí)行或者拋出異常等。

hook Class總結(jié):到這里就把hook Class的過(guò)程解析完成了,說(shuō)到底過(guò)程就是處理要被hook的類,同時(shí)把類的消息轉(zhuǎn)發(fā)方法forwardInvocation:替換成__ASPECTS_ARE_BEING_CALLED__函數(shù)。

4.hook selector過(guò)程

在核心函數(shù)aspect_prepareClassAndHookSelector()中的hook Class處理在上面已經(jīng)解析完成,現(xiàn)在我們來(lái)繼續(xù)往下解析hook selector。這一部分源碼如下:

Method targetMethod = class_getInstanceMethod(klass, selector);
IMP targetMethodIMP = method_getImplementation(targetMethod);
if (!aspect_isMsgForwardIMP(targetMethodIMP)) {
    //當(dāng)前imp不是消息轉(zhuǎn)發(fā)方法
    //獲取當(dāng)前原始的selector對(duì)應(yīng)的IMP的方法編碼typeEncoding
    const char *typeEncoding = method_getTypeEncoding(targetMethod);
    
    //給原始方法添加一個(gè)前綴名"aspects__XX"
    SEL aliasSelector = aspect_aliasForSelector(selector);
    
    if (![klass instancesRespondToSelector:aliasSelector]) {
        //沒(méi)有找到新方法"aspects__XX",就添加一個(gè)新方法
        __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);
    }
    
    //我們使用消息轉(zhuǎn)發(fā)forwardInvocation來(lái)進(jìn)行hook
    //把當(dāng)前的sel方法的替換成forwardInvocation方法,selector被執(zhí)行的時(shí)候,直接會(huì)觸發(fā)消息轉(zhuǎn)發(fā)從而進(jìn)入forwardInvocation
    class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
    AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
}
  • 1.獲取要被hook的方法Method以及函數(shù)實(shí)現(xiàn)IMP指針

  • 2.判斷當(dāng)前方法實(shí)現(xiàn)是不是消息轉(zhuǎn)發(fā)方法,如果是直接返回不作處理,hook不成功。不是繼續(xù)往下執(zhí)行3

  • 3.獲取原始的selector對(duì)應(yīng)的IMP的方法編碼typeEncoding,以及給原始方法添加一個(gè)前綴名"aspects__XX",獲取SEL,這個(gè)sel保存了原始方法的實(shí)現(xiàn)。這樣才hook的過(guò)程中,如果不成功會(huì)調(diào)用這個(gè)sel,走原始代碼,不會(huì)影響正常函數(shù)。

  • 4.如果沒(méi)有找到這個(gè)方法就調(diào)用class_addMethod給這個(gè)Class添加一個(gè)新方法.

  • 5.調(diào)用class_replaceMethod函數(shù)來(lái)把selector的函數(shù)實(shí)現(xiàn)替換成forwardInvocation:,這樣調(diào)用selector時(shí)就回走forwardInvocation:函數(shù),而這個(gè)函數(shù)在hook Class的過(guò)程中也被替換成__ASPECTS_ARE_BEING_CALLED__函數(shù),真正實(shí)現(xiàn)的函數(shù)入口就是這個(gè)。

hook selector總結(jié):說(shuō)到底這個(gè)過(guò)程就是處理要被hook的selector,把selector方法的實(shí)現(xiàn)替換成的消息轉(zhuǎn)發(fā)方法forwardInvocation:。

5.舉例說(shuō)明核心流程:

  • 新建類A
  • 給類A添加一個(gè)對(duì)象方法method以及實(shí)現(xiàn)
  • 初始化一個(gè)A類實(shí)例對(duì)象a

我們準(zhǔn)備hook初始化階段調(diào)用下面代碼:

[a aspect_hookSelector:@selector(method) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo>aspectInfo,){
        NSLog(@"arguments = %@",aspectInfo.arguments);    
} error:NULL];
  • 1.因?yàn)閔ook的是實(shí)例方法,所以在hook Class的時(shí)候會(huì)新建子類:A__Aspects_(中間類)
  • 2.調(diào)用aspect_swizzleForwardInvocation()把A__Aspects_類的forwardInvocation:函數(shù)實(shí)現(xiàn)替換成__ASPECTS_ARE_BEING_CALLED__,
  • 3.把A__Aspects_的isa指向A,也就是把A__Aspects_類和A類一模一樣。
  • 4.把self,也就是a的所屬類變成A__Aspects_類。
  • 5.給A__Aspects_類的添加一個(gè)和原始要被hook的方法一樣的函數(shù),用于保存原始方法的實(shí)現(xiàn)
  • 6.替換A__Aspects_類的要被hook的方法,替換成forwardInvocation:函數(shù)

我們?cè)趆ook執(zhí)行過(guò)程調(diào)用下面代碼:

[a method];
  • 因?yàn)樵趆ook初始化階段時(shí),把method替換成了forwardInvocation:函數(shù)。forwardInvocation:函數(shù)又被替換成了__ASPECTS_ARE_BEING_CALLED__函數(shù)。所以[a method]的函數(shù)實(shí)現(xiàn)就是__ASPECTS_ARE_BEING_CALLED__函數(shù)。
  • __ASPECTS_ARE_BEING_CALLED__函數(shù)就是我們hook切入的具體操作。如果我們hook沒(méi)有成功時(shí),也會(huì)調(diào)用原始的selector方法或者拋出異常,不會(huì)影響正常函數(shù)的實(shí)現(xiàn)。

5.Aspects總結(jié)

Aspects核心思想就是通過(guò)runtime的消息轉(zhuǎn)發(fā)機(jī)制和method swizzling生成中間類來(lái)替換函數(shù)實(shí)現(xiàn)。這種思想和上一篇KVO的底層實(shí)現(xiàn)很相似??梢宰屑?xì)閱讀里面的代碼,學(xué)習(xí)相關(guān)的實(shí)現(xiàn)思想以及優(yōu)秀的代碼片段。

我沒(méi)有把所有的代碼都一一解析,如果想看代碼注釋的,博客最后會(huì)有注釋的項(xiàng)目地址。我對(duì)Aspects的庫(kù)的中文注釋以及理解說(shuō)明,有興趣的可以下載看下。

有注釋的Aspects地址:https://git.coding.net/Dely/JYAOPDemo.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容