iOS开发过程中,有时候一些第三方省市区位置选择器PickerView出现诡异bug:在快速同时分别滑动省、市、区各栏的时候,出现奔溃。这时候,你可以打个断点,查出问题所在。笔者碰到的原因是:数组越界。
-
这里举例的第三方省市区选择器:YLAwesomePicker于Jun22, 2017年提交的版本(该问题目前已被改开源作者于Jul 31, 2017修复)。
-
奔溃演示:
- 奔溃情景:当省一栏滑到澳门,并同时滑动第二栏第三栏时,直接崩溃。
这里记录修复这种bug的一种方案。首先看看出问题的源代码,然后指出问题所在,并给出修复方案。
1. 问题代码
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if(component < _componentCount){
//remember the select data
NSArray *array = _dataConfiguration.dataSource[@(component)];
YLAwesomeData *currentData = array[row];
if(_tmpSelectedData.count > component){
_tmpSelectedData[component] = currentData;
}else{
[_tmpSelectedData addObject:currentData];
}
//ex: when select first component, the second to the last component data need refresh
for(NSInteger nextCom = component + 1; nextCom < _componentCount; nextCom++){
//reload data
NSArray *nextArray = [_dataConfiguration dataInComponent:nextCom selectedComponent:component row:row];
[pickerView reloadComponent:nextCom];
if(nextArray.count > 0){
NSInteger nextSelectRow = 0;
//remember the select data
YLAwesomeData *nextData = nextArray[nextSelectRow];
if(_tmpSelectedData.count > nextCom){
_tmpSelectedData[nextCom] = nextData;
}else{
[_tmpSelectedData addObject:nextData];
}
[pickerView selectRow:nextSelectRow inComponent:nextCom animated:NO];
}else{
if(_tmpSelectedData.count > nextCom){
//remove the last selected data
[_tmpSelectedData removeObjectsInRange:NSMakeRange(nextCom, _tmpSelectedData.count - nextCom)];
}
}
}
}
}
2. 问题所在
奔溃出现,在于这两句:
NSArray *array = _dataConfiguration.dataSource[@(component)];
YLAwesomeData *currentData = array[row];
如上所示,在获取array之前,并没有判断array是否为空数组?为空,array[row]
能正常取值吗?所以添加一个判断即可:
if (array && array.count > 0) {
...
}
还有,array[row]中的row超过数组元素个数怎么办?继续追加判断:array.count>0且array.count>row。又有row>=0,故而如下即可:
if (array && array.count > row) {
...
}
完整修复代码
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if(component < _componentCount){
//remember the select data
NSArray *array = _dataConfiguration.dataSource[@(component)];
if (array && array.count > row) {
YLAwesomeData *currentData = array[row];
if(_tmpSelectedData.count > component){
_tmpSelectedData[component] = currentData;
}else{
[_tmpSelectedData addObject:currentData];
}
//ex: when select first component, the second to the last component data need refresh
for(NSInteger nextCom = component + 1; nextCom < _componentCount; nextCom++){
//reload data
NSArray *nextArray = [_dataConfiguration dataInComponent:nextCom selectedComponent:component row:row];
[pickerView reloadComponent:nextCom];
if(nextArray.count > 0){
NSInteger nextSelectRow = 0;
//remember the select data
YLAwesomeData *nextData = nextArray[nextSelectRow];
if(_tmpSelectedData.count > nextCom){
_tmpSelectedData[nextCom] = nextData;
}else{
[_tmpSelectedData addObject:nextData];
}
[pickerView selectRow:nextSelectRow inComponent:nextCom animated:NO];
}else{
if(_tmpSelectedData.count > nextCom){
//remove the last selected data
[_tmpSelectedData removeObjectsInRange:NSMakeRange(nextCom, _tmpSelectedData.count - nextCom)];
}
}
}
}
}
}
3. 定位奔溃技巧
这里介绍一下为了定位奔溃原因的捕获异常断点技巧:
1. 添加异常断点
左边栏上面点击断点标签,然后点击左下角+号按钮添加断点:
2. 选择异常类型
选择断点捕获类型,按下图设置即可。当然你也可以只选择OC或者Swift异常。
4. 小结
举一反三,不仅仅是位置选择器,在通过网络获取数据并为本地模型赋值的时候,如果没有严谨在赋值取值之前判断一些对象是否为空,就经常会出现这样的崩溃。
拓展文献
- 刨根问底:OC 中如何判断 NSArray 为空
http://www.jianshu.com/p/ba11a53777e1