综合出现NSScanner: nil string argument libc++abi.dylib: terminat错误的解决方案
在开发中出现了这个错误,断点查找很久,没找到问题所在的代码,google下,发现了下面这几点会产生这个错误:
- 首先,顾名思义,错误原因是我们在调用某个方法的时候,传入了一个空字符串(注意区别于字符串内容为空)作为方法参数。
- 对某一个空数组使用objectAtIndex方法。不会报数组越界的错,而是NSScanner: nil string argument。
经过检查,我代码中如果字符串赋值,我一般都对nil做了一定处理,用@""代替,也未有数组越界,但是还是报这样的错误。
现在的信息点是libc++abi.dylib,这个库到底是做什么的?从后缀看,是一个动态库,那么会不会是因为发生了一些动态错误?而按经验来看,一般的动态错误基本是因为动态类型错误引起,在object-c语言中,会发生动态类型错误的可能基本存在于不可变类型与可变类型之间的转换,那么我们的查错范围将优先限制在不可变类型与可变类型转换上,是否我们对一个不可变类型进行了修改操作?当然,编译器没有那么傻,如果直接对一个不可变类型进行修改操作,是会直接报错的,那么就剩下另一种可能,程序将一个不可变类型赋值给可变类型,然后对可变类型进行了修改操作,这样可以通过静态检查,但是动态运行的时候,就会发生类型错误。基于以上分析,我们可以跟踪断点,会发现程序在对mutable对象进行add、set等操作时挂掉,而这个对象实际上赋值的是一个不可变对象。常见的情况是把一个NSArray对象赋值给一个NSMutableArray对象,然后进行了delete、add等修改操作,或者把一个NSDictionary对象赋值给一个NSMutableDictionary对象,然后进行了set等操作。
这是从http://www.itnose.net/detail/6196671.html里面得来的方案,经过排查,代码表面上也未有这样的错误。
然后找到最后崩溃处的断点代码如下:
NSDictionary *orderDict = [notification userInfo]; self.selectedOrderDict = orderDict; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.selectedRow inSection:0]; [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:@[indexPath], nil] withRowAnimation:UITableViewRowAnimationNone];
灵机一动,其实是实在不知道怎么改了,于是改成了下面的代码,然后编译,运行,既然可以了。。。。。。
NSDictionary *orderDict = [notification userInfo]; self.selectedOrderDict = orderDict; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.selectedRow inSection:0]; NSMutableArray *tmpArray = [NSMutableArray array]; [tmpArray addObject:indexPath]; [self.tableView reloadRowsAtIndexPaths:tmpArray withRowAnimation:UITableViewRowAnimationNone];
不解,既然这样就可以了,可是从代码层次、逻辑层次来说,这两种写法应该是一样的,难道是这个借口需要的是可变的数组?于是打开里面的接口,发现是这样的:
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0);
当时我的心情是崩溃的,尼玛,那啥意思,于是有了下面的测试:
NSDictionary *orderDict = [notification userInfo]; self.selectedOrderDict = orderDict; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.selectedRow inSection:0]; // NSMutableArray *tmpArray = [NSMutableArray array]; // [tmpArray addObject:indexPath]; // [self.tableView reloadRowsAtIndexPaths:tmpArray withRowAnimation:UITableViewRowAnimationNone]; NSArray *tmpArray = @[indexPath]; [self.tableView reloadRowsAtIndexPaths:tmpArray withRowAnimation:UITableViewRowAnimationNone];
这样也是不崩溃的......
好吧,到这,还是没发现错误原因,然后回去仔细看看面前的代码,发现
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.selectedRow inSection:0]; [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:@[indexPath], nil] withRowAnimation:UITableViewRowAnimationNone];
indexPath本身就是个对象了,而我还用NSValues包装它,放进去,肯定不能解析而崩溃啊......