Aspect

AOP:通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)锄禽。

AOP對(duì)業(yè)務(wù)處理過程中的切面進(jìn)行提取,他所面對(duì)的是處理過程中的某個(gè)步驟或階段箱蟆。以獲得邏輯過程中各部分之間的耦合性的隔離效果沟绪。

OOP和AOP屬于兩個(gè)不同的“思考方式”。OOP專注于對(duì)象的屬性和行為的封裝空猜,AOP專注于處理某個(gè)步驟和階段的绽慈,從中進(jìn)行切面的提取恨旱。

講解:

Aspects是一個(gè)輕量級(jí)的面向切面編程的庫(kù)。它能允許你在每一個(gè)類和每一個(gè)實(shí)例中存在的方法里面加入任何代碼坝疼∷严停可以在以下切入點(diǎn)插入代碼:before(在原始的方法前執(zhí)行) / instead(替換原始的方法執(zhí)行) / after(在原始的方法后執(zhí)行,默認(rèn))。通過Runtime消息轉(zhuǎn)發(fā)實(shí)現(xiàn)Hook钝凶。Aspects會(huì)自動(dòng)的調(diào)用super方法仪芒,使用method swizzling起來(lái)會(huì)更加方便。

Aspects 的方案就是耕陷,對(duì)于待 hook 的 selector掂名,將其指向 objc_msgForward / _objc_msgForward_stret ,同時(shí)生成一個(gè)新的 aliasSelector 指向原來(lái)的 IMP,并且 hook 住 forwardInvocation 函數(shù)哟沫,使他指向自己的實(shí)現(xiàn)饺蔑。按照上面的思路,當(dāng)被 hook 的 selector 被執(zhí)行的時(shí)候嗜诀,首先根據(jù) selector 找到了 objc_msgForward / _objc_msgForward_stret ,而這個(gè)會(huì)觸發(fā)消息轉(zhuǎn)發(fā)猾警,從而進(jìn)入 forwardInvocation。同時(shí)由于 forwardInvocation 的指向也被修改了隆敢,因此會(huì)轉(zhuǎn)入新的 forwardInvocation 函數(shù)发皿,在里面執(zhí)行需要嵌入的附加代碼,完成之后拂蝎,再轉(zhuǎn)回原來(lái)的 IMP穴墅。

swizzing forwardInvocation:
aspect_hookClass 函數(shù)主要swizzing類/對(duì)象的forwardInvocation函數(shù)。aspects的真正的處理邏輯都是在forwardInvocation函數(shù)里面進(jìn)行的温自。對(duì)于對(duì)象實(shí)例而言封救,源代碼中并沒有直接 swizzling 對(duì)象的 forwardInvocation 方法,而是動(dòng)態(tài)生成一個(gè)當(dāng)前對(duì)象的子類捣作,并將當(dāng)前對(duì)象與子類關(guān)聯(lián),然后替換子類的 forwardInvocation 方法(這里具體方法就是調(diào)用了 object_setClass(self, subclass) ,將當(dāng)前對(duì)象 isa 指針指向了 subclass ,同時(shí)修改了 subclass 以及其 subclass metaclass 的 class 方法,使他返回當(dāng)前對(duì)象的 class。,這個(gè)地方特別繞鹅士,它的原理有點(diǎn)類似 kvo 的實(shí)現(xiàn)券躁,它想要實(shí)現(xiàn)的效果就是,將當(dāng)前對(duì)象變成一個(gè) subclass 的實(shí)例掉盅,同時(shí)對(duì)于外部使用者而言也拜,又能把它繼續(xù)當(dāng)成原對(duì)象在使用,而且所有的 swizzling 操作都發(fā)生在子類趾痘,這樣做的好處是你不需要去更改對(duì)象本身的類慢哈,也就是,當(dāng)你在 remove aspects 的時(shí)候永票,如果發(fā)現(xiàn)當(dāng)前對(duì)象的 aspect 都被移除了卵贱,那么滥沫,你可以將 isa 指針重新指回對(duì)象本身的類,從而消除了該對(duì)象的 swizzling ,同時(shí)也不會(huì)影響到其他該類的不同對(duì)象)键俱。對(duì)于每一個(gè)對(duì)象而言兰绣,這樣的動(dòng)態(tài)對(duì)象只會(huì)生成一次,這里 aspect_swizzlingForwardInvocation 將使得 forwardInvocation 方法指向 aspects 自己的實(shí)現(xiàn)邏輯 ,

swizzling selector
當(dāng) forwradInvocation 被 hook 之后编振,接下來(lái)缀辩,將對(duì)傳入的 selector 進(jìn)行 hook ,這里的做法是踪央,將 selector 指向了轉(zhuǎn)發(fā) IMP 臀玄,同時(shí)生成一個(gè) aliasSelector ,指向了原來(lái)的 IMP ,同時(shí)為了防止重復(fù) hook ,做了一個(gè)判斷畅蹂,如果發(fā)現(xiàn) selector 已經(jīng)指向了轉(zhuǎn)發(fā) IMP ,那就就不需要進(jìn)行交換了

這里講解下幾個(gè)函數(shù)的用法

 // 添加方法
 /** 
 第一個(gè)參數(shù): cls:給哪個(gè)類添加方法
 第二個(gè)參數(shù): SEL name:添加方法的名稱
 第三個(gè)參數(shù): IMP imp: 方法的實(shí)現(xiàn)健无,函數(shù)入口,函數(shù)名可與方法名不同(建議與方法名相同)
 第四個(gè)參數(shù): types :方法類型魁莉,需要用特定符號(hào)睬涧,參考API
 */
 BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
 
 
 替換 class 的 sel 對(duì)應(yīng)的函數(shù)指針,返回值為 sel 對(duì)應(yīng)的原函數(shù)指針
 class_replaceMethod(Class cls, SEL name, IMP imp, const char * types) 
 
直接替換 method 的函數(shù)指針
method_setImplementation(Method method, IMP imp)

NSMethodSignature和 NSInvocation進(jìn)行 method 或 block的調(diào)用
NSMethodSignature概述:
NSMethodSignature用于描述method的類型信息:返回值類型旗唁,以及每個(gè)參數(shù)的類型畦浓。
 //獲取方法簽名
-(void)test
{
    //獲取方法簽名
    NSMethodSignature *sigOfPrintStr = [self methodSignatureForSelector:@selector(printStr:)];
    //獲取方法簽名對(duì)應(yīng)的invocation
    NSInvocation *invocationPrint = [NSInvocation invocationWithMethodSignature:sigOfPrintStr];
    //設(shè)置消息的接收者
    [invocationPrint setTarget:self];
    [invocationPrint setSelector:@selector(printStr:)];
    //設(shè)置參數(shù)
    //對(duì)NSInvocation對(duì)象設(shè)置的參數(shù)個(gè)數(shù)及類型和獲取的返回值的類型要與創(chuàng)建對(duì)象時(shí)使用的NSMethodSignature對(duì)象代表的參數(shù)及返回值類型向一致,否則
    NSString *str = @"helloWord" ;
    [invocationPrint setArgument:&str atIndex:2];
    [invocationPrint invoke];


    //Block調(diào)用方式
    void(^block1)(int) = ^(int a)
    {
        NSLog(@"block1 %d",a);
    };
    
    NSMethodSignature *signature = aspect_blockMethodSignature(block1,nil);
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    [invocation setTarget:block1];
    int a=2;
    //由block生成的NSInvocation對(duì)象的第一個(gè)參數(shù)是block本身检疫,剩下的為 block自身的參數(shù)讶请。
    [invocation setArgument:&a atIndex:1];
    [invocation invoke];
    

}
-(void)printStr:(NSString *)str
{
    NSLog(@"打印%@",str);
}
//代碼來(lái)自 Aspect
// Block internals.
typedef NS_OPTIONS(int, AspectBlockFlags) {
    AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),
    AspectBlockFlagsHasSignature          = (1 << 30)
};
typedef struct _AspectBlock {
    __unused Class isa;
    AspectBlockFlags flags;
    __unused int reserved;
    void (__unused *invoke)(struct _AspectBlock *block, ...);
    struct {
        unsigned long int reserved;
        unsigned long int size;
        // requires AspectBlockFlagsHasCopyDisposeHelpers
        void (*copy)(void *dst, const void *src);
        void (*dispose)(const void *);
        // requires AspectBlockFlagsHasSignature
        const char *signature;
        const char *layout;
    } *descriptor;
    // imported variables
} *AspectBlockRef;


static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
    AspectBlockRef layout = (__bridge void *)block;
    if (!(layout->flags & AspectBlockFlagsHasSignature)) {
        //NSString *description = [NSString stringWithFormat:@"The block %@ doesn't contain a type signature.", block];
        //AspectError(AspectErrorMissingBlockSignature, description);
        return nil;
    }
    void *desc = layout->descriptor;
    desc += 2 * sizeof(unsigned long int);
    if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {
        desc += 2 * sizeof(void *);
    }
    if (!desc) {
        NSString *description = [NSString stringWithFormat:@"The block %@ doesn't has a type signature.", block];
        //AspectError(AspectErrorMissingBlockSignature, description);
        return nil;
    }
    const char *signature = (*(const char **)desc);
    return [NSMethodSignature signatureWithObjCTypes:signature];
}

Objective-C Method 的 Type 信息以 “返回值 Type + 參數(shù) Types” 的形式組合編碼,還需要考慮到 self 和 _cmd 這兩個(gè)隱含參數(shù):

AspectInfo 類講解

把外面?zhèn)鬟M(jìn)來(lái)的實(shí)例instance屎媳,和原始的invocation保存到AspectInfo類對(duì)應(yīng)的成員變量中夺溢。
- (NSArray *)arguments方法是一個(gè)懶加載,返回的是原始的invocation里面的aspects參數(shù)數(shù)組烛谊。

Type Encodings作為對(duì)RunTime的補(bǔ)充,編譯器將每個(gè)方法的返回值和參數(shù)類型編碼為一個(gè)字符串风响,并將其與方法的selector關(guān)聯(lián)一起。

  • (int)tapWithView:(double)pointx; => "i@:d"


    參數(shù)類型.png

[圖片上傳失敗...(image-1d56c6-1534995690199)]
AspectInfo里面主要是 NSInvocation 信息丹禀。將NSInvocation包裝一層状勤,比如參數(shù)信息等。

AspectIdentifier講解

AspectIdentifier是一個(gè)切片Aspect的具體內(nèi)容双泪。里面會(huì)包含了單個(gè)的 Aspect 的具體信息持搜,包括執(zhí)行時(shí)機(jī),要執(zhí)行 block 所需要用到的具體信息:包括方法簽名焙矛、參數(shù)等等葫盼。初始化AspectIdentifier的過程實(shí)質(zhì)是把我們傳入的block打包成AspectIdentifier祟昭。

方法流程:

    - aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error
    └── aspect_add(self, selector, options, block, error);
        └── aspect_performLocked
            ├── aspect_isSelectorAllowedAndTrack
            └── aspect_prepareClassAndHookSelector
> 生成一個(gè)AspectIdentifier
> 獲取到blockSignature

AspectsContainer講解

按照切面的時(shí)機(jī)分別把切片Aspects放到對(duì)應(yīng)的數(shù)組里面署隘。removeAspects會(huì)循環(huán)移除所有的Aspects。

AspectTracker講解

AspectTracker這個(gè)類是用來(lái)跟蹤要被hook的類

objc_setAssociatedObject(self, aliasSelector, aspectContainer, OBJC_ASSOCIATION_RETAIN);
四個(gè)參數(shù) 源對(duì)象 關(guān)鍵字 關(guān)聯(lián)的對(duì)象 關(guān)聯(lián)策略 

五. Aspects hook過程詳解

- aspect_prepareClassAndHookSelector(self, selector, error);
  ├── aspect_hookClass(self, error)
  │    ├──aspect_swizzleClassInPlace
  │    ├──aspect_swizzleForwardInvocation
  │    │  └──__ASPECTS_ARE_BEING_CALLED__
  │    │       ├──aspect_aliasForSelector
  │    │       ├──aspect_getContainerForClass
  │    │       ├──aspect_invoke
  │    │       └──aspect_remove
  │    └── aspect_hookedGetClass
  ├── aspect_isMsgForwardIMP
  ├──aspect_aliasForSelector(selector)
  └── aspect_getMsgForwardIMP
28_6.png

[圖片上傳失敗...(image-f94b38-1534995690200)]
傳送門
Demo注解: https://github.com/laotang013/AspectsDemo.git
https://halfrost.com/ios_aspect/
https://wereadteam.github.io/2016/06/30/Aspects/
http://www.reibang.com/p/cd431dc5fd6e
http://www.reibang.com/p/a6b675f4d073

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市女仰,隨后出現(xiàn)的幾起案子励背,更是在濱河造成了極大的恐慌闽烙,老刑警劉巖每界,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異钱反,居然都是意外死亡掖看,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門面哥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)哎壳,“玉大人,你說(shuō)我怎么就攤上這事尚卫」殚牛” “怎么了?”我有些...
    開封第一講書人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵吱涉,是天一觀的道長(zhǎng)刹泄。 經(jīng)常有香客問我,道長(zhǎng)怎爵,這世上最難降的妖魔是什么特石? 我笑而不...
    開封第一講書人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮鳖链,結(jié)果婚禮上姆蘸,老公的妹妹穿的比我還像新娘。我一直安慰自己芙委,他們只是感情好逞敷,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著灌侣,像睡著了一般推捐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上侧啼,一...
    開封第一講書人閱讀 49,850評(píng)論 1 290
  • 那天玖姑,我揣著相機(jī)與錄音,去河邊找鬼慨菱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛戴甩,可吹牛的內(nèi)容都是我干的符喝。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼甜孤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼协饲!你這毒婦竟也來(lái)了畏腕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤茉稠,失蹤者是張志新(化名)和其女友劉穎描馅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體而线,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铭污,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了膀篮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘹狞。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖誓竿,靈堂內(nèi)的尸體忽然破棺而出磅网,到底是詐尸還是另有隱情,我是刑警寧澤筷屡,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布涧偷,位于F島的核電站,受9級(jí)特大地震影響毙死,放射性物質(zhì)發(fā)生泄漏燎潮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一规哲、第九天 我趴在偏房一處隱蔽的房頂上張望跟啤。 院中可真熱鬧,春花似錦唉锌、人聲如沸隅肥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)腥放。三九已至,卻和暖如春绿语,著一層夾襖步出監(jiān)牢的瞬間秃症,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工吕粹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留种柑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓匹耕,卻偏偏與公主長(zhǎng)得像聚请,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349

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