不起眼的nil的潛在風(fēng)險(xiǎn)

我們知道,在ObjC中向nil發(fā)送任何消息都不會(huì)導(dǎo)致崩潰,然而,在某些情況下這可能只是南柯一夢(mèng)~

此次的問(wèn)題,就發(fā)生我們的項(xiàng)目使用鏈?zhǔn)紸PI之后

鏈?zhǔn)紸PI的使用

如下為一個(gè)使用了鏈?zhǔn)紸PI的People類代碼:

@interface People : NSObject

- (People *(^)(NSString *sth))eat;

@end

@implementation People

- (People *(^)(NSString *sth))eat 
{
    return ^(NSString *sth) {
        NSLog(@"I eat %@", sth);
        return self;
    };
}

@end

要說(shuō)的是,以上代碼并沒有什么問(wèn)題的,鏈?zhǔn)紸PI十分的簡(jiǎn)潔好用。但是在以下情景中使用鏈?zhǔn)紸PI,可能整個(gè)App都不好了

int main(int argc, char * argv[])
{
    People *a = [[People alloc] init];
    a.eat(@"蘋果").eat(@"香蕉").eat(@"橘子");
    a = nil;
    a.eat(@"香蕉");
}

將a置為nil后,它就無(wú)福消受大香蕉了。此時(shí)的結(jié)果也只有一個(gè),奔潰~~~~~~~

報(bào)錯(cuò)信息如下:

error: Execution was interrupted, reason: Attempted to dereference an invalid pointer..
The process has been returned to the state before expression evaluation.

ObjC中的nil

ObjC中向nil對(duì)象發(fā)送任何消息都不會(huì)崩潰,不用懷疑,這并沒有什么問(wèn)題,網(wǎng)絡(luò)上也有大量文章介紹其原理。但是此處為什么崩潰了呢?
原因是、其實(shí) a.eat(@"蘋果") 并不是單純的一次消息發(fā)送,而是做了以下兩步操作:

  • 第一步:調(diào)用a.eat,可以理解為取得這個(gè)block
  • 第二步:傳參并執(zhí)行這個(gè)block

顯然,問(wèn)題就出在了第二步上,在a為nil時(shí),a.eat為nil,第二步就等價(jià)執(zhí)行了nil(@"蘋果")。所以,崩的也不冤。

所以在此類場(chǎng)景下,建議優(yōu)先判斷指針是否為nil,再?zèng)Q定是否執(zhí)行之后的操作。還有另外一個(gè)原因是:一次if判斷相較于消息發(fā)送來(lái)講是非??斓牟僮髁?。實(shí)測(cè)代碼和結(jié)果如下:

int main(int argc, char * argv[])
{
    /// 測(cè)試次數(shù)
    int testCount = 100000000;
    /// 每次測(cè)試中,消息發(fā)送的執(zhí)行次數(shù)
    int executeCount = 1;
    People *x = [[People alloc] init];
    People *y = nil;
    
    NSDate *date1 = [NSDate date];
    // 測(cè)試A:消息發(fā)送,sayHello為一空方法
    for (int i = 0; i < testCount; i++) {
        for (int j = 0; j < executeCount; j++) {
            [x sayHello];
        }
    }
    NSDate *date2 = [NSDate date];
    // 測(cè)試B:消息發(fā)送,對(duì)象為nil
    for (int i = 0; i < testCount; i++) {
        for (int j = 0; j < executeCount; j++) {
            [y sayHello];
        }
    }
    NSDate *date3 = [NSDate date];
    // 測(cè)試C:if判空,不執(zhí)行消息發(fā)送
    for (int i = 0; i < testCount; i++) {
        if (y) {    // 執(zhí)行if判斷后,可避免n多條無(wú)意義語(yǔ)句的執(zhí)行
            for (int j = 0; j < executeCount; j++) {
                [y sayHello];
            }
        }
    }
    NSDate *date4 = [NSDate date];
    
    // 打印結(jié)果
    NSLog(@"A (!= nil): %lf", [date2 timeIntervalSinceDate:date1]);
    NSLog(@"B ( = nil): %lf", [date3 timeIntervalSinceDate:date2]);
    NSLog(@"C (nil+if): %lf", [date4 timeIntervalSinceDate:date3]);
}

測(cè)試結(jié)果如下(各執(zhí)行1,0000,0000次測(cè)試,executeCount為每輪測(cè)試中方法的執(zhí)行次數(shù)):

executeCount A (執(zhí)行空方法) B (Object為nil) C (nil+if判空)
1 0.607867 0.491741 0.203444
10 3.852057 3.015501 0.210288
50 20.307179 15.178183 0.205914
  • 由A、B可知,向nil發(fā)送消息還是比較慢的操作;
  • 但B、C可知,if判斷不涉及消息發(fā)送,執(zhí)行速度非??欤矣纱丝杀苊舛鄺l無(wú)意義語(yǔ)句的執(zhí)行(向nil發(fā)送消息),帶來(lái)是時(shí)間紅利更是明顯。

針對(duì)此種情景的解決方案

上文已經(jīng)提到通過(guò)判斷對(duì)象是否為nil,再?zèng)Q定執(zhí)行后續(xù)操作這一解決方案,這也最為簡(jiǎn)單高效。但是這一方案較容易出現(xiàn)漏判的情況,所以以下兩種配合的方案也值得考慮:

  • 1、根據(jù)業(yè)務(wù)場(chǎng)景將鏈?zhǔn)紸PI抽出到OC方法中統(tǒng)一執(zhí)行,這樣如果對(duì)象為nil時(shí),就到不了執(zhí)行鏈?zhǔn)紸PI這一步了。同時(shí)這樣也有助于功能模塊的進(jìn)一步細(xì)分;
  • 2、確保對(duì)象釋放后,有關(guān)該對(duì)象的所有邏輯操作已取消(比如頁(yè)面銷毀時(shí)結(jié)束所有未完成的網(wǎng)絡(luò)請(qǐng)求、DB操作),這個(gè)要結(jié)合具體業(yè)務(wù)場(chǎng)景進(jìn)行處理。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,030評(píng)論 0 9
  • 摘自: http://www.cocoachina.com/ios/20150803/12872.html 說(shuō)明...
    program袁閱讀 928評(píng)論 1 3
  • __block和__weak修飾符的區(qū)別其實(shí)是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用,...
    LZM輪回閱讀 3,584評(píng)論 0 6
  • 有人的地方就有江湖,這句話不假,和一群整天勾心斗角的女人相處在一個(gè)辦公室,言多必失
    天差地別閱讀 224評(píng)論 0 0
  • 酉戌穿定義為: 酉戌 :本來(lái)是土生金的關(guān)系 。當(dāng)酉合辰 ,戌恨死了 ;戌合卯 ,酉恨死了 。 酉戌 :遵循禮儀 ,...
    加菲貓的鏟屎君閱讀 14,265評(píng)論 2 52

友情鏈接更多精彩內(nèi)容