『ios』NSInvocation NSMethodSignature 全面解析

『ios』-objc_msgSend + 消息轉(zhuǎn)發(fā) 全面解析(二)

timg.jpg

對(duì)于 NSInvocation 之前的意識(shí)一直很模糊构舟,雖然在消息轉(zhuǎn)發(fā)中用過肝箱,但是缺的就是沉下心來,分析一波扶关。now,let's go.

先來分析一波api

@class NSMethodSignature;

NS_ASSUME_NONNULL_BEGIN

NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available")
@interface NSInvocation : NSObject {
@private
    void *_frame;
    void *_retdata;
    id _signature;
    id _container;
    uint8_t _retainedArgs;
    uint8_t _reserved[15];
}

+ (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)sig;//初始化方法

@property (readonly, retain) NSMethodSignature *methodSignature;//方法簽名

- (void)retainArguments;//防止參數(shù)釋放掉
@property (readonly) BOOL argumentsRetained;

@property (nullable, assign) id target; //target
@property SEL selector;//方法

- (void)getReturnValue:(void *)retLoc;//獲取返回值
- (void)setReturnValue:(void *)retLoc;//設(shè)置返回值

- (void)getArgument:(void *)argumentLocation atIndex:(NSInteger)idx;//獲取參數(shù)
- (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx;//設(shè)置參數(shù)

- (void)invoke; //執(zhí)行
- (void)invokeWithTarget:(id)target;// target發(fā)送消息残吩,即target執(zhí)行方法

@end

從初始化方法+ (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)sig;來看生棍,我們需要 NSMethodSignature *這個(gè)對(duì)象。
那么下個(gè)方法簽名

NS_ASSUME_NONNULL_BEGIN

NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available")
@interface NSMethodSignature : NSObject {
@private
    void *_private;
    void *_reserved[5];
    unsigned long _flags;
}

+ (nullable NSMethodSignature *)signatureWithObjCTypes:(const char *)types;//初始化方法

@property (readonly) NSUInteger numberOfArguments;//參數(shù)數(shù)量
- (const char *)getArgumentTypeAtIndex:(NSUInteger)idx NS_RETURNS_INNER_POINTER;//獲取參數(shù)類型

@property (readonly) NSUInteger frameLength;

- (BOOL)isOneway;// 是否是單向

@property (readonly) const char *methodReturnType NS_RETURNS_INNER_POINTER;//返回值類型
@property (readonly) NSUInteger methodReturnLength;//返回長(zhǎng)度

@end

api看完了猪钮,我覺得有些東西還是得從方法中來看才能學(xué)到東西。

-(NSString *)doit:(NSInteger)test1 doit2:(NSString *)test2{
    return @"2";
}
- (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個(gè)隱藏參數(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ù)類型判斷然后解析成對(duì)應(yīng)類型烤低,這里默認(rèn)所有參數(shù)均為OC對(duì)象
            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];
    }
    // 需要做返回類型判斷。比如返回值為常量需要包裝成對(duì)象笆载,這里僅以最簡(jiǎn)單的`@`為例
    return returnVal;
}

直接用po看打印扑馁。


image.png

從上面我們可以看到,invocation有四個(gè)參數(shù)凉驻,target selector argument argument.
然后分別對(duì)應(yīng)這四個(gè)符號(hào)腻要。@ : q @.

看到這符號(hào)也許你有點(diǎn)蒙蔽。

NSMethodSignature的初始化方法涝登。

+ (nullable NSMethodSignature *)signatureWithObjCTypes:(const char *)types;

那么上面的這些符號(hào)就是types.就拿上面這個(gè)方法舉例子雄家。

 NSMethodSignature *signature =  [NSMethodSignature signatureWithObjCTypes:@"@@:q@"];
第一個(gè)@是返回值NSString *
第二個(gè)@是target
第三個(gè):是Selector
第四個(gè)q 是nsintager
第五個(gè)@是nsstring *

對(duì)了好像還沒有展示NSMethodSignature的結(jié)構(gòu)。


image.png

當(dāng)然還有其他兩個(gè)

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");

+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");

說到這其實(shí)對(duì)于NSInvocation應(yīng)該就可以運(yùn)用自如了吧胀滚。
上面的例子中的方法趟济,是為了解決performSelector中的傳值問題的。


image.png

實(shí)際項(xiàng)目中咽笼,我們用的地方就應(yīng)該是消息轉(zhuǎn)發(fā)的過程中顷编。

正好看到一個(gè)挺好的例子。
動(dòng)態(tài)實(shí)現(xiàn)set get方法剑刑。

     data = [[NSMutableDictionary alloc] init];
        [data setObject:@"Tom Sawyer" forKey:@"title"];
        [data setObject:@"Mark Twain" forKey:@"author"];
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
    NSString *sel = NSStringFromSelector(selector);
    if ([sel rangeOfString:@"set"].location == 0) {
        //動(dòng)態(tài)造一個(gè) setter函數(shù)
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    } else {
        //動(dòng)態(tài)造一個(gè) getter函數(shù)
        return [NSMethodSignature signatureWithObjCTypes:"@@:"];
    }
}
 
- (void)forwardInvocation:(NSInvocation *)invocation
{
    //拿到函數(shù)名
    NSString *key = NSStringFromSelector([invocation selector]);
    if ([key rangeOfString:@"set"].location == 0) {
        //setter函數(shù)形如 setXXX: 拆掉 set和冒號(hào) 
        key = [[key substringWithRange:NSMakeRange(3, [key length]-4)] lowercaseString];
        NSString *obj;
        //從參數(shù)列表中找到值
        [invocation getArgument:&obj atIndex:2];
        [data setObject:obj forKey:key];
    } else {
        //getter函數(shù)就相對(duì)簡(jiǎn)單了媳纬,直接把函數(shù)名做 key就好了。
        NSString *obj = [data objectForKey:key];
        [invocation setReturnValue:&obj];
    }
}
 

最后,附上某位大神寫的對(duì)于block钮惠,應(yīng)該怎么應(yīng)用

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];
    CTBlockDescription *ct = [[CTBlockDescription alloc] initWithBlock:target];
    NSMethodSignature *methodSignature = ct.blockSignature;
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
    
    invocation.target = target;
    [invocation retainArguments];
    // invocation 有1個(gè)隱藏參數(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ù)類型判斷然后解析成對(duì)應(yīng)類型,這里默認(rèn)所有參數(shù)均為OC對(duì)象
            if (strcmp(type, "@") == 0) {
                id argument = arguments[I];
                [invocation setArgument:&argument atIndex:1 + I];
            }
        }
    }
    
    [invocation invoke];
    
   __weak  id  returnVal;
    
//        printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(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];
    }
    
    NSString *returnStr = returnVal;
   
    printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(returnVal)));
     printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(returnStr)));
     printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(target)));
    // 需要做返回類型判斷萌腿。比如返回值為常量需要包裝成對(duì)象,這里僅以最簡(jiǎn)單的`@`為例
    return returnStr;
}

上面代碼經(jīng)過測(cè)試抖苦,


image.png

這個(gè)地方如果不改為__weak的話就會(huì)崩潰毁菱。
下面附上相關(guān)打印


image.png

可以看到invocation有三個(gè)參數(shù)。因?yàn)閎lock沒有selector锌历。
上面的這個(gè)地方我注釋掉了贮庞,因?yàn)檫@是私有api。過不了審哦究西。
//    const char *_Block_signature(void *);
//    const char *signature = _Block_signature((__bridge void *)target);
//
//    NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:signature];
//    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];

未完待續(xù)窗慎。。卤材。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末遮斥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扇丛,更是在濱河造成了極大的恐慌术吗,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帆精,死亡現(xiàn)場(chǎng)離奇詭異较屿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)卓练,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門隘蝎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人襟企,你說我怎么就攤上這事嘱么。” “怎么了顽悼?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵拱撵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我表蝙,道長(zhǎng)拴测,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任府蛇,我火速辦了婚禮集索,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己务荆,他們只是感情好妆距,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著函匕,像睡著了一般娱据。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盅惜,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天中剩,我揣著相機(jī)與錄音,去河邊找鬼抒寂。 笑死结啼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的屈芜。 我是一名探鬼主播郊愧,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼井佑!你這毒婦竟也來了属铁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤躬翁,失蹤者是張志新(化名)和其女友劉穎红选,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姆另,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喇肋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迹辐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蝶防。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖明吩,靈堂內(nèi)的尸體忽然破棺而出间学,到底是詐尸還是另有隱情,我是刑警寧澤印荔,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布低葫,位于F島的核電站,受9級(jí)特大地震影響仍律,放射性物質(zhì)發(fā)生泄漏嘿悬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一水泉、第九天 我趴在偏房一處隱蔽的房頂上張望善涨。 院中可真熱鬧窒盐,春花似錦、人聲如沸钢拧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)源内。三九已至葡粒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間膜钓,已是汗流浹背嗽交。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留呻此,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓腔寡,卻偏偏與公主長(zhǎng)得像焚鲜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子放前,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉忿磅,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,690評(píng)論 0 9
  • 摘要:Aspects用來干什么?Aspect是一個(gè)簡(jiǎn)潔高效的用于使iOS支持AOP(面向切面編程)的框架。官方描述...
    京北磊哥閱讀 859評(píng)論 0 1
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡(jiǎn)介 Runt...
    樂樂的簡(jiǎn)書閱讀 2,132評(píng)論 0 9
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 751評(píng)論 0 1
  • 去年有段時(shí)間得空凭语,就把谷歌GAE的API權(quán)威指南看了一遍葱她,收獲頗豐,特別是在自己幾乎獨(dú)立開發(fā)了公司的云數(shù)據(jù)中心之后...
    騎單車的勛爵閱讀 20,480評(píng)論 0 41