iOS小知識(五)-[NSString stringWithFormat:@"%ld",[value integerValue]]竟會崩?

接手了一個古老的項目。這個項目跑了六七年,裏面充滿了MRC的autorelease和一些古老的寫法。最近改一個bug,這個bug很簡單,就是客戶端model的字段類型是NSString,但某些情況下,服務器會返回NSNumber類型,後續客戶端有用這個字段進行isEqualToString 所以直接就崩了。

這是一個我們正常項目基本不會遇到的問題。。。
正常項目中YYModel或者其他Model會幫我們自動轉換數據類型,哪還碰到過這種老古董。
我接手後,很自然而然的進行了修改。。。

原問題代碼:NSString *type = [root objectForKey:@"type"];
我修改後代碼:NSString * type = [NSString stringWithFormat:@"%ld",[[root objectForKey:@"type"] integerValue]];

因爲type本來返回就是@"0",@"1",@"2"這種,所以我強轉下類型,覺得解決掉了。
但在合併代碼Review的環節中,同事跟我說,這樣寫有風險,還是可能會崩,建議我改成

建議修改後代碼:NSString * type = [NSString stringWithFormat:@"%@",[root objectForKey:@"type"]];

嗯???寫了這麼多年代碼,沒見這麼寫崩過的嘛。
後面,我寫了個Demo進行驗證:
測試代碼

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor redColor];
    [self doDataTestDemo];
}

- (void)doDataTestDemo{
    [self testDemo:nil];
    [self testDemo:@"adsfsdfaf"];
    [self testDemo:@(1)];
    [self testDemo:@"a"];
    [self testDemo:NULL];
    [self testDemo:[NSNull null]];
}

- (void)testDemo:(id)value{
    NSLog(@"傳進來的value=%@",value);
    NSString *str1 = [NSString stringWithFormat:@"%@",value];
    NSString *str2 = [NSString stringWithFormat:@"%ld",[value integerValue]];
    if ([str1 isEqualToString:@"1"]) {
        NSLog(@"str1相等=%@",str1);
    }else{
        NSLog(@"str1不相等=%@",str1);
    }
    if ([str2 isEqualToString:@"1"]) {
        NSLog(@"str2相等=%@",str2);
    }else{
        NSLog(@"str2不相等=%@",str2);
    }
}
控制檯輸出
2021-04-15 17:52:43.030584+0800 TestDemo[1837:657964] 傳進來的value=(null)
2021-04-15 17:52:43.030686+0800 TestDemo[1837:657964] str1不相等=(null)
2021-04-15 17:52:43.030717+0800 TestDemo[1837:657964] str2不相等=0
2021-04-15 17:52:43.030744+0800 TestDemo[1837:657964] 傳進來的value=adsfsdfaf
2021-04-15 17:52:43.030818+0800 TestDemo[1837:657964] str1不相等=adsfsdfaf
2021-04-15 17:52:43.030856+0800 TestDemo[1837:657964] str2不相等=0
2021-04-15 17:52:43.030885+0800 TestDemo[1837:657964] 傳進來的value=1
2021-04-15 17:52:43.031024+0800 TestDemo[1837:657964] str1相等=1
2021-04-15 17:52:43.031313+0800 TestDemo[1837:657964] str2相等=1
2021-04-15 17:52:43.031356+0800 TestDemo[1837:657964] 傳進來的value=a
2021-04-15 17:52:43.031435+0800 TestDemo[1837:657964] str1不相等=a
2021-04-15 17:52:43.031534+0800 TestDemo[1837:657964] str2不相等=0
2021-04-15 17:52:43.031681+0800 TestDemo[1837:657964] 傳進來的value=(null)
2021-04-15 17:52:43.031926+0800 TestDemo[1837:657964] str1不相等=(null)
2021-04-15 17:52:43.032106+0800 TestDemo[1837:657964] str2不相等=0
在最後一組測試數據時,[self testDemo:[NSNull null]]會進行崩潰

好吧,確實會崩,因爲[NSNull null]返回的是一個繼承NSObject對象,所以肯定會崩。報方法不識別

2021-04-15 18:00:52.223725+0800 TestDemo[1840:659292] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull integerValue]: unrecognized selector sent to instance 0x1f18ebad8'

那確實當服務器給你返回個null的時候,客戶端可能就玩蛇皮了。。。但是即便用NSString stringWithFormat:@"%@"的方法,在服務器靠不住的場景下,項目裏也可能會到處充斥着(null)這樣的UI顯示。我們當然有很多種解決方案,比如重構代碼,模型換YYModel,給NSNull 加分類,runtime判斷null等等各種方法,但礙於現實中的種種,最後還是決定就這樣吧。
在此,也重新記錄一下,nil、Nil、NULL和[NSNull null]的區別

  • nil
    • 當一個對象是nil的時候,雖然內存地址已經被收回。但我們依然可以向它發消息,並不會引起崩潰。
  • Nil
    • 本質和nil一樣,一般將對象置空爲nil,類置空爲Nil
  • NULL
    • C語音產物,空指針
  • [NSNull null]
    • 本質是一個值爲空的對象。它繼承自NSObject,它和以上的區別在於,它並不是空指針,而是一個值爲空的對象。既然是對象,那麼當調用不屬於它的方法時,就會Crash,本例中崩潰原因也是因爲此。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章