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

我們這篇博客繼續(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。具體思路就是和上面的類方法流程一樣。
-
1.新建一個(gè)要被hook的類的子類:
上面是Aspects的核心思想以及流程的簡(jiǎn)單說(shuō)明,下面我們對(duì)這些核心代碼進(jìn)行梳理介紹。
注意:
- Aspects并不能hook類的類方法
- Aspects不能hook靜態(tài)方法
- Aspects不能hook類中不存在或者未實(shí)現(xiàn)的方法
4.Aspects核心代碼解析

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)如下:

- 函數(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:
- 一個(gè)是hook Class的處理,這一部分主要實(shí)現(xiàn)上面提到的
3.hook Class過(guò)程
我們先來(lái)看一下核心函數(shù)aspect_prepareClassAndHookSelector()中的第一部分hook Class過(guò)程,這個(gè)會(huì)調(diào)用aspect_hookClass函數(shù)

上面根據(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ò)程,流程圖如下:

上面都會(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