iOS 中的错误分为 NSError 和 NSException,其中NSError用于可恢复的错误,而NSException则是不可恢复的错误,对于NSException我们可以通过NSSetUncaughtExceptionHandler来进行崩溃的捕获获取,那么常见的 NSException 有哪些呢?
- 数组越界访问
NSArray *array = @[@"2"];
NSString *aa = array[2];
// 对应崩溃错误信息
// Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 0]'
- KVC赋值给一个没有定义的key
[[UIViewController new] setValue:@"Jack" forKey:@"name"];
// 对应的崩溃错误信息
// Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UIViewController 0x7fd7b2f4a0d0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key name.'
- 字典中键或值插入空值 nil
NSString *key1 = nil;
NSString *value1 = nil;
NSDictionary *dic1 = @{key1:@"USA"};
// 对应的崩溃信息
// Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]'
或者
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setObject:value1 forKey:@"USA"];
也会发生崩溃,但是通过KVC的方式如
[dic setValue:value1 forKey:@"USA"];
却不会发生崩溃,对于字典的KVC方法可以查看
/* Send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObjectForKey:.
*/
- (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;
所以相对健壮
- NSAttributedString初始化时传入空值nil
NSString *string = nil;
NSAttributedString *AttributedString = [[NSAttributedString alloc] initWithString:string];
// 对应的崩溃信息
// Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'NSConcreteAttributedString initWithString:: nil value'
- unrecognized selector
[[UIViewController new] performSelector:@selector(test)];
// 对应的崩溃信息
// Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIViewController test]: unrecognized selector sent to instance 0x7fe479706a10'
- UITableView 中没有注册cell重用标识,取空的cell为空
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// 之前并没有在tableView中注册该重用标识
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell1" forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:@"第%zd行",indexPath.row];
return cell;
}
// 对应的崩溃信息
// *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier cell1 - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
另外更新UITableView数据时,前后的数据不同步也会导致该类型的错误
- Push到同一个控制器多次
TableViewController *TC = [TableViewController new];
[self.navigationController pushViewController:TC animated:YES];
[self.navigationController pushViewController:TC animated:YES];
[self.navigationController pushViewController:TC animated:YES];
// 对应的崩溃信息
// Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Pushing the same view controller instance more than once is not supported (<TableViewController: 0x7f84c551aaa0>)'
那么对于iOS 提供的 try-catch 可以捕获上述哪几种错误呢?
- unrecognized selector
- NSAttributedString初始化时传入空值nil
- 数组越界访问
- KVC赋值给一个没有定义的key
- 字典中键或值插入空值 nil
- NSAttributedString初始化时传入空值nil
可见try-catch来说,其能抓取的错误是有限的,对于内存溢出和野指针问题无能为力;除此之外,try-catch还可能产生以下问题
- 即使在ARC下,内存释放的代码会因为try-catch的中断不能执行,会造成内存泄漏;对于采用GC来释放内存的java等语言,不会存在此问题
- exception使用block造成额外的开销,效率较低
基于上述原因,iOS中的try-catch使用并不如Android开发中使用的那么频繁