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

435 阅读4分钟

接手了一个古老的项目。这个项目跑了六七年,里面充满了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,本例中崩溃原因也是因为此。