OC方法攔截器

本文將介紹oc中的方法攔截器的實現(xiàn)思路,如何攔截一個方法福贞,并在執(zhí)行方法前進行一些前置業(yè)務邏輯后決定是否繼續(xù)執(zhí)行方法。

緣由:最近公司需要將組件進行授權管理(類似第三方SDK的授權),之前編寫過很多內部使用的SDK乍钻,現(xiàn)在需要將這些SDK通過授權管理起來咖杂,我的想法是編寫一個統(tǒng)一授權管理的模塊庆寺,需要被管理的SDK引用這個授權管理模塊即可,要求是對原SDK盡量少做代碼改動诉字。
在百度搜了一圈都沒有想要的答案懦尝,所以決定自己提需求自己實現(xiàn)。

需求:

1.對需要被攔截監(jiān)聽的方法無代碼侵入壤圃。

2.成功攔截需要被監(jiān)聽的方法陵霉,并在方法執(zhí)行前做前置邏輯判斷,最后決定是否執(zhí)行被攔截的方法伍绳。

實現(xiàn)思路:

這么刁鉆的需求肯定用runtime了踊挠,思路就是將被監(jiān)聽的方法的實例利用方法交換替換成自定義的方法,在自定義方法中執(zhí)行我們自己的邏輯判斷墨叛,最后決定是否需要繼續(xù)原方法調用止毕。(是不是很簡單模蜡?上才藝!)

這里只講實例方法的攔截扁凛,因為任何類都可能需要注冊自己需要被監(jiān)聽的方法忍疾,所以創(chuàng)建一個NSObject的分類

NSObject+SRModuleAuthorization.h:


@interface NSObject (SRModuleAuthorization)

- (void)registerNeedMonitorSEL:(SEL)sel;

@end

頭文件只對外暴露一個注冊需要被監(jiān)聽方法的API。

下面來看實現(xiàn)NSObject+SRModuleAuthorization.m:

//注冊需要被攔截的方法
- (void)registerNeedMonitorSEL:(SEL)sel {
    //獲取方法的名稱谨朝,根據(jù)該名稱設置自定義的方法名稱
    NSString *methodName = NSStringFromSelector(sel);
    NSString *newMethodName = [NSString stringWithFormat:@"sr_auth_%@", methodName];
    //獲取自定義方法的實例
    SEL newSel = NSSelectorFromString(newMethodName);
    Method newMethod = class_getInstanceMethod([self class], newSel);
    //判斷是否已經添加了自定義的方法卤妒,如果為空就添加自定義方法
    if (!newMethod) {
        //獲取被攔截方法的結構體對象
        Method method = class_getInstanceMethod(self.class, sel);
        //獲取被攔截方法的方法簽名字符串
        const char *functionType = method_getTypeEncoding(method);
        NSString *functionTypeStr = [NSString stringWithUTF8String:functionType];
        //判斷方法簽名的字符串中,:8是否是最后兩個字符字币,
        //如果是則代表無參數(shù)方法则披,否則是有參方法,
        //針對這兩種方法添加不同的自定義攔截方法洗出。
        NSRange range = [functionTypeStr rangeOfString:@":8"];
        Method intercetionMethod;
        if (range.location + range.length < functionTypeStr.length) {//有參的添加有參攔截方法
            intercetionMethod = class_getInstanceMethod(self.class, @selector(interception:));
        } else {//添加無參攔截方法
            intercetionMethod = class_getInstanceMethod(self.class, @selector(interception));
        }
        //獲取自定義攔截方法的實例士复,
        //并將自定義方法的selector與實例關聯(lián),動態(tài)地添加進當前類型翩活。
        IMP imp = method_getImplementation(intercetionMethod);
        class_addMethod([self class], newSel, imp, functionType);
        //最后重新獲取自定義方法的方法結構體阱洪,
        //并與被監(jiān)聽方法進行實例交換,交換后菠镇,
        //被監(jiān)聽的方法被調用時冗荸,就會執(zhí)行我們自定義的方法實例,即interception
        Method method2 = class_getInstanceMethod(self.class, newSel);
        method_exchangeImplementations(method, method2);
    }
}

下面是interception方法的實現(xiàn)利耍,flag就理解為一個開關就行蚌本,interception的主要功能是決定是否需要執(zhí)行被攔截的方法。

//無參攔截方法
- (void)interception {
    if (flag) {
        NSString *methodName = NSStringFromSelector(_cmd);
        NSString *newMethodName = [NSString stringWithFormat:@"dl_auth_%@", methodName];
        SEL newSel = NSSelectorFromString(newMethodName);
        NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:newSel];
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        invocation.target = self;
        invocation.selector = newSel;
        [invocation invoke];
    } else {
       //執(zhí)行自定義代碼
    }
}

有參攔截方法的參數(shù)是一個可變參數(shù)列表隘梨,我們通過遍歷并設置invocation對象的參數(shù)已達到傳參的目的程癌。

注意:這里有個問題,被監(jiān)聽的方法中如果參數(shù)類型不是對象類型的話會報錯出嘹,所以本文的方法攔截只適用于方法簽名中參數(shù)都是對象類型的方法席楚。(目前還沒有找到能夠解決的辦法,如果你讀到這里有更好的解決方案税稼,希望你能告訴我)烦秩。

//有參攔截方法
- (void)interception:(id)param, ... {
    if (flag) {
        NSString *methodName = NSStringFromSelector(_cmd);
        NSString *newMethodName = [NSString stringWithFormat:@"dl_auth_%@", methodName];
        SEL newSel = NSSelectorFromString(newMethodName);
        NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:newSel];
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        invocation.target = self;
        invocation.selector = newSel;
        NSInteger argCount = signature.numberOfArguments;
        va_list args;
        va_start(args, param);
        NSMutableArray *params = [NSMutableArray array];
        if (argCount > 2) {
            [params addObject:param];
        }
        for (int i = 3; i < argCount; i++) {
            param = va_arg(args, id);
            [params addObject:param];
        }
        va_end(args);
        for (int i = 0; i < params.count; i++) {
            id obj = params[i];
            [invocation setArgument:&obj atIndex:i + 2];
        }
        [invocation invoke];
    } else {
        //執(zhí)行自定義代碼
    }
}

以上就是我的統(tǒng)一授權組件的實現(xiàn)代碼,使用的時候就很方便了郎仆,只需要在要被監(jiān)聽的類的初始化方法中只祠,調用self的registerNeedMonitorSEL方法就行了,需要監(jiān)聽那個方法就監(jiān)聽哪個扰肌。
示例

[self registerNeedMonitorSEL:@selector(test)];

你也可以編寫一個監(jiān)聽所有方法的方法:

注意:如果方法簽名中有非對象的入?yún)t會報錯抛寝。

- (void)registerNeedMonitorAllMethod {
    unsigned int count;
    Method *methodList = class_copyMethodList([self class], &count);
    for (int i = 0; i < count; i++) {
        Method method = methodList[i];
        SEL selector = method_getName(method);
        [self registerNeedMonitorSEL:selector];
    }
}

//調用時一句代碼搞定
[self registerNeedMonitorAllMethod];
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
禁止轉載,如需轉載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末盗舰,一起剝皮案震驚了整個濱河市晶府,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钻趋,老刑警劉巖川陆,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蛮位,居然都是意外死亡较沪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門失仁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尸曼,“玉大人,你說我怎么就攤上這事萄焦】亟危” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵楷扬,是天一觀的道長解幽。 經常有香客問我,道長烘苹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任片部,我火速辦了婚禮镣衡,結果婚禮上,老公的妹妹穿的比我還像新娘档悠。我一直安慰自己廊鸥,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布辖所。 她就那樣靜靜地躺著惰说,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缘回。 梳的紋絲不亂的頭發(fā)上吆视,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音酥宴,去河邊找鬼啦吧。 笑死,一個胖子當著我的面吹牛拙寡,可吹牛的內容都是我干的授滓。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼般堆!你這毒婦竟也來了在孝?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤淮摔,失蹤者是張志新(化名)和其女友劉穎私沮,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體噩咪,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡顾彰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了胃碾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涨享。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖仆百,靈堂內的尸體忽然破棺而出厕隧,到底是詐尸還是另有隱情,我是刑警寧澤俄周,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布吁讨,位于F島的核電站,受9級特大地震影響峦朗,放射性物質發(fā)生泄漏建丧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一波势、第九天 我趴在偏房一處隱蔽的房頂上張望翎朱。 院中可真熱鬧,春花似錦尺铣、人聲如沸拴曲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽澈灼。三九已至,卻和暖如春店溢,著一層夾襖步出監(jiān)牢的瞬間叁熔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工逞怨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留者疤,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓叠赦,卻偏偏與公主長得像驹马,于是被迫代替她去往敵國和親革砸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345