iOS開(kāi)發(fā)-NSInvocation獲取返回值后崩潰的問(wèn)題

注:方法的參數(shù)及返回值需為對(duì)象,否則id接收的時(shí)候會(huì)報(bào)錯(cuò)

在學(xué)習(xí)NSInvocation的時(shí)候,給NSObject添加了一個(gè)category方法,如下所示

/** 系統(tǒng)提供的perform系列方法參數(shù)個(gè)數(shù)有限,可以利用NSInvocation實(shí)現(xiàn)多參數(shù) */

- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects {
    // 初始化方法簽名
    NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector];
    
    // 如果方法不存在
    if (!signature) {
        // 拋出異常
        NSString *reason = [NSString stringWithFormat:@"方法不存在 : %@",NSStringFromSelector(aSelector)];
        @throw [NSException exceptionWithName:@"error" reason:reason userInfo:nil];
    }
    
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = self;
    invocation.selector = aSelector;
    
    // 參數(shù)個(gè)數(shù)signature.numberOfArguments 默認(rèn)有一個(gè)_cmd 一個(gè)target 所以要-2
    NSInteger paramsCount = signature.numberOfArguments - 2;
    
    // 當(dāng)objects的個(gè)數(shù)多于函數(shù)的參數(shù)的時(shí)候,取前面的參數(shù)
    // 當(dāng)objects的個(gè)數(shù)少于函數(shù)的參數(shù)的時(shí)候,不需要設(shè)置,默認(rèn)為nil
    paramsCount = MIN(paramsCount, objects.count);
    
    for (NSInteger index = 0; index < paramsCount; index++) {
        id object = objects[index];
        // 對(duì)參數(shù)為nil的處理
        if ([object isKindOfClass:[NSNull class]]) {
            continue;
        }
        [invocation setArgument:&object atIndex:index + 2];
    }
    
    // 調(diào)用方法
    [invocation invoke];
    
    // 獲取返回值
    id  returnValue = nil;
    //signature.methodReturnLength == 0 說(shuō)明給方法沒(méi)有返回值
    if (signature.methodReturnLength) {
        //獲取返回值
        [invocation getReturnValue:&returnValue];
    }
    return returnValue;
}

然后美滋滋的寫(xiě)了一個(gè)多參數(shù)的方法

#pragma mark - Test Method
- (NSString *)connectStrings:(NSString *)a b:(NSString *)b c:(NSString *)c {
    return [NSString stringWithFormat:@"%@%@%@",a,b,c];
}

調(diào)用

NSString *str = [self performSelector:@selector(connectStrings:b:c:) withObjects:@[@"hello ",@"everyone,",@"good morning"]];
 NSLog(@"%@",str);

當(dāng)當(dāng)當(dāng)當(dāng)!!! Crash! 而且控制臺(tái)沒(méi)有打印任何錯(cuò)誤信息!小萌新當(dāng)場(chǎng)懵逼!
經(jīng)過(guò)我一系列的縝(bai)密(du)分(sou)析(suo),問(wèn)題得以解決.步驟如下

1.打開(kāi)僵尸模式調(diào)試,得以打印出崩潰信息

*** -[CFString release]: message sent to deallocated instance 0x6000008488e0

2.在這里已經(jīng)大致猜測(cè)到可能是方法返回值被提前釋放了 于是打印地址信息

//打印對(duì)象的內(nèi)存地址
NSLog(@"內(nèi)存地址1:%p",str);
//打印指針自己的內(nèi)存地址
NSLog(@"內(nèi)存地址2:%x",&str);

對(duì)比之后發(fā)現(xiàn)剛好是返回值的內(nèi)存地址,那么什么原因?qū)е碌哪?

3.于是又經(jīng)過(guò)一波縝(bai)密(du)分(sou)析(suo),發(fā)現(xiàn)這一段代碼存在問(wèn)題

// 獲取返回值
id  returnValue = nil;
//signature.methodReturnLength == 0 說(shuō)明給方法沒(méi)有返回值
if (signature.methodReturnLength) {
      //獲取返回值
      [invocation getReturnValue:&returnValue];
} 
return returnValue;

原因是在arc模式下缝呕,getReturnValue:僅僅是從invocation的返回值拷貝到指定的內(nèi)存地址,如果返回值是一個(gè)NSObject對(duì)象的話,是沒(méi)有處理起內(nèi)存管理的。而我們?cè)诙xreturnValue時(shí)使用的是__strong類型的指針對(duì)象,arc就會(huì)假設(shè)該內(nèi)存塊已被retain(實(shí)際沒(méi)有)雹姊,當(dāng)returnValue出了定義域釋放時(shí),導(dǎo)致該crash。假如在定義之前有賦值的話手销,還會(huì)造成內(nèi)存泄露的問(wèn)題。

修改如下

 // 獲取返回值
    id __unsafe_unretained returnValue = nil;
    //signature.methodReturnLength == 0 說(shuō)明給方法沒(méi)有返回值
    if (signature.methodReturnLength) {
        //獲取返回值
        [invocation getReturnValue:&returnValue];
    }
    id value = returnValue;
    return value;

或者

void *returnValue = NULL;
if (signature.methodReturnLength) {
    [invocation getReturnValue:&returnValue];
}
return (__bridge id)returnValue;

至此,問(wèn)題解決.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末图张,一起剝皮案震驚了整個(gè)濱河市锋拖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌祸轮,老刑警劉巖兽埃,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異适袜,居然都是意外死亡柄错,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)售貌,“玉大人给猾,你說(shuō)我怎么就攤上這事∷炭纾” “怎么了敢伸?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)恒削。 經(jīng)常有香客問(wèn)我池颈,道長(zhǎng),這世上最難降的妖魔是什么钓丰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任饶辙,我火速辦了婚禮,結(jié)果婚禮上斑粱,老公的妹妹穿的比我還像新娘弃揽。我一直安慰自己,他們只是感情好则北,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布矿微。 她就那樣靜靜地躺著,像睡著了一般尚揣。 火紅的嫁衣襯著肌膚如雪涌矢。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天快骗,我揣著相機(jī)與錄音娜庇,去河邊找鬼。 笑死方篮,一個(gè)胖子當(dāng)著我的面吹牛名秀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播藕溅,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼匕得,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了巾表?” 一聲冷哼從身側(cè)響起汁掠,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎集币,沒(méi)想到半個(gè)月后考阱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鞠苟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年乞榨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秽之。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姜凄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出趾访,到底是詐尸還是另有隱情态秧,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布扼鞋,位于F島的核電站申鱼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏云头。R本人自食惡果不足惜捐友,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望溃槐。 院中可真熱鬧匣砖,春花似錦、人聲如沸昏滴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谣殊。三九已至拂共,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間姻几,已是汗流浹背宜狐。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蛇捌,地道東北人抚恒。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像络拌,于是被迫代替她去往敵國(guó)和親柑爸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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