溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

objc方法怎么聲明和實(shí)現(xiàn)由于參數(shù)類型不一致所引發(fā)的崩潰

發(fā)布時(shí)間:2023-03-31 15:43:58 來源:億速云 閱讀:83 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“objc方法怎么聲明和實(shí)現(xiàn)由于參數(shù)類型不一致所引發(fā)的崩潰”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“objc方法怎么聲明和實(shí)現(xiàn)由于參數(shù)類型不一致所引發(fā)的崩潰”吧!

正文

你有注意過objc方法聲明處和方法實(shí)現(xiàn)處參數(shù)類型不一致的情況嗎,就像這樣:

@interface Person : NSObject
- (void)frothTime:(NSInteger)regionTime value1:(BOOL)value;
@end
@implementation Person
- (void)frothTime:(NSInteger)regionTime value1:(NSString *)value;
@end

這2個(gè)方法除了第2個(gè)參數(shù)的類型不一樣,其它都一樣,但一旦調(diào)用這個(gè)方法就會(huì)產(chǎn)生一個(gè)壞內(nèi)存訪問的崩潰,這是為什么呢?

這是我在真實(shí)項(xiàng)目中遇到的1個(gè)很有意思的問題,只要調(diào)用分類中的某個(gè)方法就百分百崩潰,而且控制臺(tái)沒有任何有用的報(bào)錯(cuò)信息,被調(diào)用的方法里面的代碼也都沒有執(zhí)行,非常難調(diào)試,我花了一些時(shí)間才弄懂了其中的原理,整理后分享出來,希望能幫到你,崩潰如下圖所示:

objc方法怎么聲明和實(shí)現(xiàn)由于參數(shù)類型不一致所引發(fā)的崩潰

以下是我簡(jiǎn)寫后的代碼,它是一份完整的代碼并且可以直接運(yùn)行。

@interface Person : NSObject
- (void)frothTime:(NSInteger)regionTime value1:(BOOL)value;
@end
@interface Person (Category)
- (void)frothTime:(NSInteger)regionTime;
- (void)frothTime:(NSInteger)regionTime value1:(NSString *)value;
@end
@implementation Person
- (void)frothTime:(NSInteger)regionTime value1:(BOOL)value {
    NSLog(@"%s", __func__);
}
@end
@implementation Person (Category)
- (void)frothTime:(NSInteger)regionTime {
    [self frothTime:regionTime value1:@"111"];
}
- (void)frothTime:(NSInteger)regionTime value1:(NSString *)value {
    NSLog(@"%s", __func__);
}
@end
int main(int argc, const char * argv[]) {
    Person *p = [[Person alloc] init];
    [p frothTime:123];
    return 0;
}

分析

運(yùn)行代碼后,會(huì)在 - (void)frothTime:(NSInteger)regionTime value1:(NSString *)value 這行代碼處產(chǎn)生一條 EXC_BAD_ACCESS 崩潰問題,通過打印和斷點(diǎn),可以看出方法內(nèi)的代碼并沒有執(zhí)行,說明是調(diào)用這個(gè)方法時(shí)發(fā)生的崩潰,所以可以排除是方法內(nèi)的代碼問題。

崩潰前的代碼位置是 [self frothTime:regionTime value1:@"111"];,這行代碼從表面上看沒有任何問題,如果你把示例代碼粘貼到 xcode 中,編譯器可能會(huì)在這行代碼后面給出1個(gè)警告: "Incompatible pointer to integer conversion sending 'NSString *' to parameter of type 'BOOL' (aka 'signed char')",意思是說方法接收的是一個(gè) BOOL 類型的參數(shù),而你傳了一個(gè) NSString * 類型。

仔細(xì)看一下代碼,你會(huì)發(fā)現(xiàn) Person 類中聲明了 - (void)frothTime:(NSInteger)regionTime value1:(BOOL)value;,而且分類中也有一個(gè)類似的聲明 - (void)frothTime:(NSInteger)regionTime value1:(NSString *)value;,它們除了第2個(gè)參數(shù)類型不一樣,其它都是一樣的;熟悉objc的同學(xué)應(yīng)該都知道,objc是沒有方法重載的概念,也就是說分類中的方法其實(shí)和類中的方法,它們的方法簽名都是 frothTime:value1:。

現(xiàn)在有2個(gè)同名的方法實(shí)現(xiàn),那么 [self frothTime:regionTime value1:@"111"]; 到底調(diào)用哪個(gè)方法呢?按照 xcode 給出的提示,似乎是調(diào)用 - (void)frothTime:(NSInteger)regionTime value1:(BOOL)value; 這個(gè)方法,因?yàn)榫幾g器提示第2個(gè)參數(shù)類型不一致。

有些同學(xué)在這里或許有一個(gè)疑問,明明有2個(gè)方法,而且分類中的方法明顯更適合調(diào)用方,為什么編譯器認(rèn)為我們調(diào)用的是類中的方法而不是分類中的方法;有2點(diǎn)原因,第1是因?yàn)閛bjc沒有方法重載的概念,所以這2個(gè)方法對(duì)編譯器來說其實(shí)都是一樣的;第2是因?yàn)閛bjc的分類是運(yùn)行時(shí)加載的,編譯器在編譯時(shí)并不知道分類以及分類方法的存在。

和其它語言不一樣,objc的方法聲明和實(shí)現(xiàn)可以重復(fù),只是不能在一個(gè)作用域中重復(fù),例如在 @interface 和 @end 就不能同時(shí)存在 - (void)frothTime:(NSInteger)regionTime value1:(BOOL)value;- (void)frothTime:(NSInteger)regionTime value1:(NSString *)value;,即使它們的參數(shù)類型并不是完全一樣;但是可以在分類中寫出和類中一樣的方法聲明或?qū)崿F(xiàn),即使你在分類中寫出 - (void)frothTime:(NSInteger)regionTime value1:(BOOL)value; 這種和類中的方法完全一模一樣的方法也不會(huì)有任何報(bào)錯(cuò)信息,如果你不小心在分類中實(shí)現(xiàn)了和類中同名的方法,那么運(yùn)行時(shí)會(huì)永遠(yuǎn)調(diào)用分類中的方法實(shí)現(xiàn),不清楚為什么的同學(xué)自行上網(wǎng)尋找答案。

現(xiàn)在我們弄明白了為什么編譯器會(huì)給出警告,也知道了實(shí)際調(diào)用的其實(shí)是分類中的方法實(shí)現(xiàn),但分類中的方法參數(shù)類型和我們傳遞的參數(shù)類型明明是一致的,那為什么還會(huì)崩潰呢?

原因在于編譯器在對(duì)代碼進(jìn)行編譯時(shí)對(duì) @"111" 這個(gè)參數(shù)是按照 BOOL 類型而不是 NSString 類型處理的,請(qǐng)看下圖:

objc方法怎么聲明和實(shí)現(xiàn)由于參數(shù)類型不一致所引發(fā)的崩潰

使用 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 文件路徑 -o 輸出的文件路徑.cpp 將objc代碼編譯為C++代碼。

可以看到編譯器把參數(shù)強(qiáng)轉(zhuǎn)成了 bool 類型,但是方法實(shí)現(xiàn)處卻是按照 NSString 類型進(jìn)行接收的,按照 NSString 類型去訪問一個(gè) bool 類型的內(nèi)存,這就是崩潰的真正原因。

補(bǔ)充

如果你嘗試將 - (void)frothTime:(NSInteger)regionTime value1:(BOOL)value; 修改為 - (void)frothTime:(NSInteger)regionTime value1:(NSObject *)value;(其實(shí)可以把value的參數(shù)類型修改為任意objc對(duì)象類型,只要不是基礎(chǔ)數(shù)據(jù)類型就行),注意:這里我只修改了方法聲明處的參數(shù)類型,并沒有修改方法實(shí)現(xiàn)處的參數(shù)類型;然后運(yùn)行項(xiàng)目;正常運(yùn)行并輸出;編譯后的代碼截圖如下:

objc方法怎么聲明和實(shí)現(xiàn)由于參數(shù)類型不一致所引發(fā)的崩潰

從截圖中可以看到參數(shù)雖然還是被強(qiáng)轉(zhuǎn)成了 NSObjet 類型,但是據(jù)我觀察,只要是objc對(duì)象都沒關(guān)系,你可以把它改為 NSArray 等任何 objc 對(duì)象類型,雖然有編譯警告,但是并不影響運(yùn)行。

另外,你也可以將 [self frothTime:regionTime value1:@"111"]; 修改為 [self performSelector:@selector(frothTime:value1:) withObject:@(regionTime) withObject:@"111"];,項(xiàng)目也可以正常運(yùn)行,原因和上面一樣,因?yàn)?withObject 的參數(shù)類型是 id。

到此,相信大家對(duì)“objc方法怎么聲明和實(shí)現(xiàn)由于參數(shù)類型不一致所引發(fā)的崩潰”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI