AOP 源碼學(xué)習(xí)筆記

合理使用oc的runtime特性堆巧,可以為我們的應(yīng)用開發(fā)和解決業(yè)務(wù)需求提供極大的便利礁叔。這篇文章權(quán)當(dāng)學(xué)習(xí)AOP源碼的學(xué)習(xí)筆記分享出來牍颈。

aop 編程值aspect第三方庫的使用解析

.h 文件聲明

AspectOptions

AspectOptions聲明了切面編程對原方法的處理方式。默認(rèn)為AspectPositionAfter琅关,在原始方法之后調(diào)用煮岁。AspectPositionInstead 替代原始方法
AspectPositionBefore 在原方法之前調(diào)用
AspectOptionAutomaticRemoval,在第一次hook之后死姚,會自動移除hook

定義的兩個協(xié)議

AspectToken:允許注銷hook人乓,協(xié)議中只有一個remove方法,返回yes則注銷hook成功都毒,返回no則注銷失敗

AspectInfo:是aop切面block的第一個參數(shù)色罚。主要包含了hook的信息。主要由instance账劲,originalInvocation戳护,arguments組成。分別是當(dāng)前hook的實例瀑焦,hook方法的原始invocation以及所有方法的參數(shù)腌且。

切面編程的兩個方法

  • (id<AspectToken>)aspect_hookSelector:(SEL)selector
    withOptions:(AspectOptions)options
    usingBlock:(id)block
    error:(NSError **)error;
  • (id<AspectToken>)aspect_hookSelector:(SEL)selector
    withOptions:(AspectOptions)options
    usingBlock:(id)block
    error:(NSError **)error;

第一個方法是為類添加一個block,第二個方法是為實例添加一個block榛瓮。兩個方法都無法為靜態(tài)方法添加hook铺董。

幾個錯誤碼

AspectErrorCode:表明返回的錯誤類型;

AspectErrorSelectorBlacklisted, /// Selectors like release, retain, autorelease are blacklisted.

AspectErrorDoesNotRespondToSelector, /// Selector could not be found.

AspectErrorSelectorDeallocPosition, /// When hooking dealloc, only AspectPositionBefore is allowed.

AspectErrorSelectorAlreadyHookedInClassHierarchy, /// Statically hooking the same method in subclasses is not allowed.

AspectErrorFailedToAllocateClassPair, /// The runtime failed creating a class pair.

AspectErrorMissingBlockSignature, /// The block misses compile time signature info and can't be called.

AspectErrorIncompatibleBlockSignature, /// The block signature does not match the method or is too large.

AspectErrorRemoveObjectAlreadyDeallocated = 100 /// (for removing) The object hooked is already deallocated.

.m文件方法解析

_AspectBlock:結(jié)構(gòu)體

AspectIdentifier:追蹤單個的aspect

包含有追蹤的selector禀晓、block精续、blockSignautre方法簽名、options切面選項粹懒、

AspectsContainer:一個對象或者類的所有切面

包含有三個數(shù)組分別用于存放hook前重付,hook替代的方法、及hook后方法

AspectTracker:所hook類的相關(guān)信息

包含類名凫乖、所有的方法名

aspects hook調(diào)用棧

以實例對象的調(diào)用為例:

  • (id<AspectToken>)aspect_hookSelector:(SEL)selector
    withOptions:(AspectOptions)options
    usingBlock:(id)block
    error:(NSError **)error{}方法為共有API确垫,調(diào)用的是
    static id aspect_add(id self, SEL selector, AspectOptions options, id block, NSError **error)弓颈。
    第一個參數(shù)傳本對象、第二個參數(shù)傳當(dāng)前hook的selector删掀、第三個傳block翔冀、第四個hook選項。
    aspect_add方法主要將參數(shù)傳入做下一步的處理爬迟。


    W

處理流程:
斷言確保傳入的參數(shù)不為空橘蜜;創(chuàng)建一個AspectIdentifier對象用于保存hook的信息。

static void aspect_performLocked(dispatch_block_t block) {
static OSSpinLock aspect_lock = OS_SPINLOCK_INIT;
OSSpinLockLock(&aspect_lock);
block();
OSSpinLockUnlock(&aspect_lock);
}

aspect_performLocked加了線程安全鎖付呕,用于保證線程安全的情況下執(zhí)行block计福。保證當(dāng)前線程只有一個在hook。


在aspect_performLocked中徽职,先是創(chuàng)建AspectsContainer對象象颖,接著將傳入的參數(shù)組裝成AspectIdentifier對象。如果對象不為空姆钉,則AspectsContainer添加該對象说订,最后執(zhí)行aspect_prepareClassAndHookSelector()方法進(jìn)行hook。

aspect_isSelectorAllowedAndTrack()

該方法判斷當(dāng)前函數(shù)能否被hook潮瓶,不能被hook的函數(shù)有retain陶冷、release、autorelease毯辅、forwardInvocation,以及delloc之后調(diào)用埂伦,并將track過的方法加入到tracker中。


先創(chuàng)建不能被hook的名單思恐,接著檢查傳入的方法是否是處于名單中的方法若是則返回No.如果hook的是delloc方法沾谜,則檢查是在delloc前還是delloc后,delloc后調(diào)用是錯誤的胀莹。


繼續(xù)判斷當(dāng)前hook的是不是類對象基跑,如果是類對象則先判斷是否在子類中hook過,如果hook過子類相同的方法則返回NO;沒有hook過則在當(dāng)前類的父類中方法查找判斷描焰。
首先拿到以當(dāng)前類為key媳否,從字典中拿到tracker,接著判斷tracker中是否含有要hook的方法如果有再判斷是否是當(dāng)前類荆秦,是則返回yes逆日,no則之前已經(jīng)hook過。

WX20190410-135709@2x.png

該方法用于記錄hook的信息萄凤。創(chuàng)建一個tracker對象,將hook過的方法加入到tracker字典中搪哪,這個字典是一個全局的單例靡努。

aspect_prepareClassAndHookSelector();

為切面編程的核心方法。


WX20190410-145805@2x.png

1、獲取當(dāng)前要hook的類
2惑朦、拿到目標(biāo)方法的Method
3兽泄、拿到目標(biāo)方法的實現(xiàn)imp地址
4、判斷目標(biāo)imp是否是消息轉(zhuǎn)發(fā)的方法漾月,若不是進(jìn)行相應(yīng)的替換
1病梢、拿到目標(biāo)方法的typeEncoding
2、為selector添加自定義前綴
3梁肿、判斷當(dāng)前類實例是否響應(yīng)添加前綴的selector如果不響應(yīng)則新增方法
4蜓陌、替換方法的實現(xiàn)為自身的方法。

aspect_aliasForSelector()

該方法主要是為selector添加上自定義的前綴 @"aspects_"

aspect_getMsgForwardIMP()

獲取消息轉(zhuǎn)發(fā)的函數(shù)實現(xiàn)吩蔑,用于替換原有的selector的方法實現(xiàn)钮热;


WX20190429-094114@2x.png

_objc_msgForward是一個函數(shù)指針,與IMP類似用于消息轉(zhuǎn)發(fā)烛芬。當(dāng)一個對象發(fā)送消息并沒有實現(xiàn)的時候隧期,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)。
該方法主要的目的是在于返回的是_objc_msgForward赘娄,還是_objc_msgForward_stret仆潮。先判斷非arm64的情況下,接著判斷返回值的大小來決定是否使用_objc_msgForward_stret遣臼。關(guān)于返回值的詳細(xì)說明可以參考官方文檔性置。關(guān)于為什么要在非arm64位的情況下判斷可以參考這篇文章

清除hook 方法

與添加hook相對應(yīng)的就是清除hook方法aspect_remove暑诸;

aspect_remove

可以發(fā)現(xiàn)首先是一個鎖aspect_performLocked蚌讼,防止多線程同時修改。具體步奏如下:
1个榕、獲取AspectsContainer對象
2篡石、根據(jù)aspect,AspectIdentifier尋找對應(yīng)的實例刪除
3西采、調(diào)用aspect_cleanupHookedClassAndSelector撤銷runtime時做的改變
4凰萨、將aspect對象重置

aspect_cleanupHookedClassAndSelector()

該方法是整個撤銷過程的核心,主要是將runtime時hook的imp復(fù)原械馆,且將tracker等置空胖眷。

綜述

以上是自己最近研究學(xué)習(xí)AOP源碼的一些認(rèn)識,受限于個人水平,如有不正確的地方,還請批評指正软吐。后續(xù)會繼續(xù)更新本篇學(xué)習(xí)筆記粘招。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市咕村,隨后出現(xiàn)的幾起案子发笔,更是在濱河造成了極大的恐慌陈轿,老刑警劉巖劳淆,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件链沼,死亡現(xiàn)場離奇詭異,居然都是意外死亡沛鸵,警方通過查閱死者的電腦和手機(jī)括勺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來曲掰,“玉大人疾捍,你說我怎么就攤上這事◎阽停” “怎么了拾氓?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長底哥。 經(jīng)常有香客問我咙鞍,道長,這世上最難降的妖魔是什么趾徽? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任续滋,我火速辦了婚禮,結(jié)果婚禮上孵奶,老公的妹妹穿的比我還像新娘疲酌。我一直安慰自己,他們只是感情好了袁,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布朗恳。 她就那樣靜靜地躺著,像睡著了一般载绿。 火紅的嫁衣襯著肌膚如雪粥诫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天崭庸,我揣著相機(jī)與錄音怀浆,去河邊找鬼。 笑死怕享,一個胖子當(dāng)著我的面吹牛执赡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播函筋,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼沙合,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了跌帐?” 一聲冷哼從身側(cè)響起首懈,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤芳来,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后猜拾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡佣盒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年挎袜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肥惭。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡盯仪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜜葱,到底是詐尸還是另有隱情全景,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布牵囤,位于F島的核電站爸黄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏揭鳞。R本人自食惡果不足惜炕贵,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望野崇。 院中可真熱鬧称开,春花似錦、人聲如沸乓梨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扶镀。三九已至蕴侣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狈惫,已是汗流浹背睛蛛。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留胧谈,地道東北人忆肾。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像菱肖,于是被迫代替她去往敵國和親客冈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容