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

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

此次的問題,就發(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

要說的是甫煞,以上代碼并沒有什么問題的菇曲,鏈?zhǔn)紸PI十分的簡潔好用。但是在以下情景中使用鏈?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后常潮,它就無福消受大香蕉了。此時(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對象發(fā)送任何消息都不會(huì)崩潰喊式,不用懷疑,這并沒有什么問題萧朝,網(wǎng)絡(luò)上也有大量文章介紹其原理岔留。但是此處為什么崩潰了呢?
原因是检柬、其實(shí) a.eat(@"蘋果") 并不是單純的一次消息發(fā)送献联,而是做了以下兩步操作:

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

顯然何址,問題就出在了第二步上里逆,在a為nil時(shí),a.eat為nil用爪,第二步就等價(jià)執(zhí)行了nil(@"蘋果")运悲。所以,崩的也不冤项钮。

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

int main(int argc, char * argv[])
{
    /// 測試次數(shù)
    int testCount = 100000000;
    /// 每次測試中亚隙,消息發(fā)送的執(zhí)行次數(shù)
    int executeCount = 1;
    People *x = [[People alloc] init];
    People *y = nil;
    
    NSDate *date1 = [NSDate date];
    // 測試A:消息發(fā)送磁餐,sayHello為一空方法
    for (int i = 0; i < testCount; i++) {
        for (int j = 0; j < executeCount; j++) {
            [x sayHello];
        }
    }
    NSDate *date2 = [NSDate date];
    // 測試B:消息發(fā)送,對象為nil
    for (int i = 0; i < testCount; i++) {
        for (int j = 0; j < executeCount; j++) {
            [y sayHello];
        }
    }
    NSDate *date3 = [NSDate date];
    // 測試C:if判空阿弃,不執(zhí)行消息發(fā)送
    for (int i = 0; i < testCount; i++) {
        if (y) {    // 執(zhí)行if判斷后诊霹,可避免n多條無意義語句的執(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]);
}

測試結(jié)果如下(各執(zhí)行1,0000,0000次測試,executeCount為每輪測試中方法的執(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í)行速度非常快怔蚌,且由此可避免多條無意義語句的執(zhí)行(向nil發(fā)送消息)巩步,帶來是時(shí)間紅利更是明顯。

針對此種情景的解決方案

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

  • 1竟闪、根據(jù)業(yè)務(wù)場景將鏈?zhǔn)紸PI抽出到OC方法中統(tǒng)一執(zhí)行,這樣如果對象為nil時(shí)芒炼,就到不了執(zhí)行鏈?zhǔn)紸PI這一步了。同時(shí)這樣也有助于功能模塊的進(jìn)一步細(xì)分术徊;
  • 2本刽、確保對象釋放后,有關(guān)該對象的所有邏輯操作已取消(比如頁面銷毀時(shí)結(jié)束所有未完成的網(wǎng)絡(luò)請求赠涮、DB操作)子寓,這個(gè)要結(jié)合具體業(yè)務(wù)場景進(jìn)行處理。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笋除,一起剝皮案震驚了整個(gè)濱河市斜友,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垃它,老刑警劉巖鲜屏,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異国拇,居然都是意外死亡洛史,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門酱吝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來也殖,“玉大人,你說我怎么就攤上這事务热∫涫龋” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵崎岂,是天一觀的道長捆毫。 經(jīng)常有香客問我,道長冲甘,這世上最難降的妖魔是什么冻璃? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任响谓,我火速辦了婚禮,結(jié)果婚禮上省艳,老公的妹妹穿的比我還像新娘娘纷。我一直安慰自己,他們只是感情好跋炕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布赖晶。 她就那樣靜靜地躺著,像睡著了一般辐烂。 火紅的嫁衣襯著肌膚如雪遏插。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天纠修,我揣著相機(jī)與錄音胳嘲,去河邊找鬼。 笑死扣草,一個(gè)胖子當(dāng)著我的面吹牛了牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辰妙,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼鹰祸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了密浑?” 一聲冷哼從身側(cè)響起蛙婴,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎尔破,沒想到半個(gè)月后街图,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡懒构,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了痴脾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颤介。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖赞赖,靈堂內(nèi)的尸體忽然破棺而出滚朵,到底是詐尸還是另有隱情,我是刑警寧澤前域,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布辕近,位于F島的核電站,受9級特大地震影響匿垄,放射性物質(zhì)發(fā)生泄漏移宅。R本人自食惡果不足惜归粉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漏峰。 院中可真熱鬧糠悼,春花似錦、人聲如沸浅乔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽靖苇。三九已至席噩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贤壁,已是汗流浹背悼枢。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脾拆,地道東北人馒索。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像假丧,于是被迫代替她去往敵國和親双揪。 傳聞我的和親對象是個(gè)殘疾皇子动羽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

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

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