強大的NSInvocation

前言

消息轉(zhuǎn)發(fā)中提到過NSInvocation這個類,這里說一下我所理解的NSInvocation宅荤。NSInvocation命令模式的一種實現(xiàn)屑迂,它包含選擇器、方法簽名冯键、相應(yīng)的參數(shù)以及目標對象惹盼。所謂的方法簽名,即方法所對應(yīng)的返回值類型和參數(shù)類型惫确。當NSInvocation被調(diào)用手报,它會在運行時通過目標對象去尋找對應(yīng)的方法蚯舱,從而確保唯一性,可以用[receiver message]來解釋掩蛤。實際開發(fā)過程中直接創(chuàng)建NSInvocation的情況不多見枉昏,這些事情通常交給系統(tǒng)來做。比如bangJSPatcharm64方法替換的實現(xiàn)就是利用runtime消息轉(zhuǎn)發(fā)最后一步中的NSInvocation實現(xiàn)的揍鸟。

正文

基于這種命令模式兄裂,可以利用NSInvocation調(diào)用任意SEL甚至block

SEL
- (id)performSelector:(SEL)aSelector withArguments:(NSArray *)arguments {
    
    if (aSelector == nil) return nil;
    NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = self;
    invocation.selector = aSelector;
    
    // invocation 有2個隱藏參數(shù)阳藻,所以 argument 從2開始
    if ([arguments isKindOfClass:[NSArray class]]) {
        NSInteger count = MIN(arguments.count, signature.numberOfArguments - 2);
        for (int i = 0; i < count; i++) {
            const char *type = [signature getArgumentTypeAtIndex:2 + i];
            
            // 需要做參數(shù)類型判斷然后解析成對應(yīng)類型晰奖,這里默認所有參數(shù)均為OC對象
            if (strcmp(type, "@") == 0) {
                id argument = arguments[i];
                [invocation setArgument:&argument atIndex:2 + i];
            }
        }
    }
    
    [invocation invoke];
    
    id returnVal;
    if (strcmp(signature.methodReturnType, "@") == 0) {
        [invocation getReturnValue:&returnVal];
    }
    // 需要做返回類型判斷。比如返回值為常量需要包裝成對象腥泥,這里僅以最簡單的`@`為例
    return returnVal;
}
運行結(jié)果

NSObject中的performSelector相比匾南,沒有了參數(shù)個數(shù)限制。

invocation
signature
block
static id invokeBlock(id block ,NSArray *arguments) {
    if (block == nil) return nil;
    id target = [block  copy];

    const char *_Block_signature(void *);
    const char *signature = _Block_signature((__bridge void *)target);

    NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:signature];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
    invocation.target = target;

    // invocation 有1個隱藏參數(shù)道川,所以 argument 從1開始
    if ([arguments isKindOfClass:[NSArray class]]) {
        NSInteger count = MIN(arguments.count, methodSignature.numberOfArguments - 1);
        for (int i = 0; i < count; i++) {
            const char *type = [methodSignature getArgumentTypeAtIndex:1 + i];
            NSString *typeStr = [NSString stringWithUTF8String:type];
            if ([typeStr containsString:@"\""]) {
                type = [typeStr substringToIndex:1].UTF8String;
            }
            
            // 需要做參數(shù)類型判斷然后解析成對應(yīng)類型午衰,這里默認所有參數(shù)均為OC對象
            if (strcmp(type, "@") == 0) {
                id argument = arguments[i];
                [invocation setArgument:&argument atIndex:1 + i];
            }
        }
    }

    [invocation invoke];

    id returnVal;
    const char *type = methodSignature.methodReturnType;
    NSString *returnType = [NSString stringWithUTF8String:type];
    if ([returnType containsString:@"\""]) {
        type = [returnType substringToIndex:1].UTF8String;
    }
    if (strcmp(type, "@") == 0) {
        [invocation getReturnValue:&returnVal];
    }
    // 需要做返回類型判斷。比如返回值為常量需要包裝成對象冒萄,這里僅以最簡單的`@`為例
    return returnVal;
}

運行結(jié)果
invocation
signature
SEL與block比較
  • invocation
    SEL既有target也有selector臊岸,block只有target
  • signature
    SEL有兩個隱藏參數(shù),類型均為@ 類型為@: 尊流,分別對應(yīng)target和selector帅戒。block有一個隱藏參數(shù),類型為@?崖技,對應(yīng)target且block的target為他本身
  • type
    以O(shè)C對象為例:SEL的type為@逻住,block的type會跟上具體類型,如@"NSString"
再談block

在block的invocation中有這樣的代碼

    const char *_Block_signature(void *);
    const char *signature = _Block_signature((__bridge void *)target);

_Block_signature其實是JavaScriptCore/ObjcRuntimeExtras.h中的私有API(這個頭文件并沒有公開可以戳這里查看)

既然蘋果把API封了迎献,那就自己實現(xiàn)咯瞎访,萬能的github早有答案CTObjectiveCRuntimeAdditions
CTBlockDescription.hCTBlockDescription.m拖到項目中,代碼這樣寫

static id invokeBlock(id block ,NSArray *arguments) {
    if (block == nil) return nil;
    id target = [block  copy];
    
    CTBlockDescription *ct = [[CTBlockDescription alloc] initWithBlock:target];
    NSMethodSignature *methodSignature = ct.blockSignature;
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
    invocation.target = target;
    
    // invocation 有1個隱藏參數(shù)吁恍,所以 argument 從1開始
    if ([arguments isKindOfClass:[NSArray class]]) {
        NSInteger count = MIN(arguments.count, methodSignature.numberOfArguments - 1);
        for (int i = 0; i < count; i++) {
            const char *type = [methodSignature getArgumentTypeAtIndex:1 + i];
            NSString *typeStr = [NSString stringWithUTF8String:type];
            if ([typeStr containsString:@"\""]) {
                type = [typeStr substringToIndex:1].UTF8String;
            }
            
            // 需要做參數(shù)類型判斷然后解析成對應(yīng)類型扒秸,這里默認所有參數(shù)均為OC對象
            if (strcmp(type, "@") == 0) {
                id argument = arguments[i];
                [invocation setArgument:&argument atIndex:1 + i];
            }
        }
    }
    
    [invocation invoke];
    
    id returnVal;
    const char *type = methodSignature.methodReturnType;
    NSString *returnType = [NSString stringWithUTF8String:type];
    if ([returnType containsString:@"\""]) {
        type = [returnType substringToIndex:1].UTF8String;
    }
    if (strcmp(type, "@") == 0) {
        [invocation getReturnValue:&returnVal];
    }
    // 需要做返回類型判斷。比如返回值為常量需要包裝成對象冀瓦,這里僅以最簡單的`@`為例
    return returnVal;
}
運行結(jié)果
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伴奥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子翼闽,更是在濱河造成了極大的恐慌拾徙,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件感局,死亡現(xiàn)場離奇詭異尼啡,居然都是意外死亡暂衡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門玄叠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來古徒,“玉大人,你說我怎么就攤上這事读恃。” “怎么了代态?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵寺惫,是天一觀的道長。 經(jīng)常有香客問我蹦疑,道長西雀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任歉摧,我火速辦了婚禮艇肴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叁温。我一直安慰自己再悼,他們只是感情好,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布膝但。 她就那樣靜靜地躺著冲九,像睡著了一般。 火紅的嫁衣襯著肌膚如雪跟束。 梳的紋絲不亂的頭發(fā)上莺奸,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音冀宴,去河邊找鬼灭贷。 笑死,一個胖子當著我的面吹牛略贮,可吹牛的內(nèi)容都是我干的甚疟。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼刨肃,長吁一口氣:“原來是場噩夢啊……” “哼古拴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起真友,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤黄痪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后盔然,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體桅打,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡是嗜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了挺尾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹅搪。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖遭铺,靈堂內(nèi)的尸體忽然破棺而出丽柿,到底是詐尸還是另有隱情,我是刑警寧澤魂挂,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布甫题,位于F島的核電站,受9級特大地震影響涂召,放射性物質(zhì)發(fā)生泄漏坠非。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一果正、第九天 我趴在偏房一處隱蔽的房頂上張望炎码。 院中可真熱鬧,春花似錦秋泳、人聲如沸潦闲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽矫钓。三九已至,卻和暖如春舍杜,著一層夾襖步出監(jiān)牢的瞬間新娜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工既绩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留概龄,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓饲握,卻偏偏與公主長得像私杜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子救欧,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉衰粹,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,690評論 0 9
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言,那么這個「動態(tài)」表現(xiàn)在哪呢笆怠?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,182評論 0 7
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,548評論 33 466
  • 繼上Runtime梳理(四) 通過前面的學(xué)習(xí)铝耻,我們了解到Objective-C的動態(tài)特性:Objective-C不...
    小名一峰閱讀 744評論 0 3
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,132評論 0 9