OC Exception

背景:

最近在整理bugly上的问题,顺便整理记录下来,方便后面开发的时候避免犯一样的错误.ios现在的系统版本都到13点几了,而我们app之前支持到ios8,后面升级到ios10.所以我们解决的问题主要还是针对ios10及以上.在解决问题的同时发现ios11以下也就是ios10点多有很大的差别.或者说ios11开始系统做了好多容错处理,好多本来应该闪退的在11及以上版本都不闪退了.如后面会提到的OKV、通知等监听,键盘和数组safe的冲突导致的闪退.

关于NSExceptionName:

常见的就4种,其他的至少在我们项目没见过.下面就先根据异常名字简单翻译理解下.

NSGenericException:一般异常

NSMallocException:内存不足异常

NSObjectInaccessibleException:对象不可访问异常

NSObjectNotAvailableException:对象不可用异常

NSDestinationInvalidException:目标无效异常

NSPortTimeoutException:端口超时异常

NSInvalidSendPortException:无效发送端口异常

NSInvalidReceivePortException:无效接收端口异常

NSPortSendException:端口发送异常

NSPortReceiveException:端口接收异常

NSOldStyleException:老样式异常

重点分享4类异常:

NSRangeException:范围异常/越界异常

问题:

使用的数组下标超出数组最大下标值

比如数组长度count, index的下标范围[0, count -1], 在开发时,可能index的最大值超过数组的范围

这可能是不同模块的处理没有对应上

使用的数组下标是一个非正常值

如果小标(index)的值是由其他模块的变量传递进来的,这就会有很大的不确定性, 可能是一个很大的整数值

很明显,上面的值分别是32位和64位下的最大整数值,可能的情况就是传递的 index 参数获取了无效的值,但仍然拿到 NSMutableArray 中使用

比如 index 通过下面的方式获取

如果找不到 str ,则返回 NSNotFound,32位下它就是2147483647,64位下18446744073709551615 ,将它作为参数传递则会导致 NSRangeException。

Cannot remove an observer for the key path"currentViewController.viewModel.selectedGoodsCount" from because it is not registered as an observer.

[self addObserver:self forKeyPath:@"currentVC.viewModel.selectedGoodsCount" options:NSKeyValueObservingOptionNew context:nil];

[self removeObserver:self forKeyPath:@"currentVC.viewModel.selectedGoodsCount"];

空数组的操作

如果一个数组刚刚初始化,还是空的,就对它进行相关读取操作

处理的数据范围 NSRange 超过数据本身的长度

-[__NSArrayM removeObjectsInRange:]: range {1, 1} extends beyond bounds [0 .. 0]

Cannot remove an observer for the key path"currentViewController.viewModel.selectedGoodsCount" from because it is not registered as an observer.

[self addObserver:self forKeyPath:@"currentVC.viewModel.selectedGoodsCount" options:NSKeyValueObservingOptionNew context:nil];

[self removeObserver:self forKeyPath:@"currentVC.viewModel.selectedGoodsCount"];

KVO监听移除了2次,或还没添加就运行了移除,也会报数据越界.一般移除代码会写在dealloc方法,添加代码比较习惯写到viewDidLoad方法.这样正常情况下是不会有问题的,但是极端情况下,手机卡了等情况如果导致你的VC创建了,当没有能加载完成显示出来就直接回收了,就会导致没有添加监听直接走移除监听而导致上面出现的闪退.所以这里给的建议是可以把监听的添加代码放在init方法处理,让生命周期对称使用.或用runtime原理来做统一的交换safe保护下哪怕移除了多次监听也不让闪退.

NSInvalidArgumentException:无效参数异常

1.运行时插入的Object为nil

2.或者调用一个没有实现的方法

3.performSegue但是没有在storyboard里面连线

4.返回的是id类型强转为另外一个类型,并调用方法出现找不到

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:'-[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance 0x60000389e500'

NSString *result=@"{\"username\":\"aaa\",\"phone\":\"18273948475\",\"bankcount\":\"k3232323\"}";

NSData *data = [result dataUsingEncoding:NSUTF8StringEncoding];

NSMutableDictionary *info = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];

if (info) {

      NSString *username = info[@"username"];//[Utils UrlDecode: info[@"username"]];

      [info setObject:username forKey:@"username"];

}


从JSON数据创建一个Foundation对象。如果解析器应该允许不是NSArray或NSDictionary的顶级对象,请设置NSJSONReadingAllowFragments选项。设置NSJSONReadingMutableContainers选项将使解析器生成可变NSArray和NSDictionary。设置NSJSONReadingMutableLeaves选项将使解析器生成可变的NSString对象。如果在解析过程中发生错误,那么将设置错误参数,结果将为零。

UITabBarController *tabBarController = (UITabBarController *)[UIApplication sharedApplication].keyWindow.rootViewController;

UINavigationController *navigationController = tabBarController.selectedViewController;

像这种强转就要特别注意,如果当前的rootViewController不是UITabBarController类型就会出现闪退.

正常写代码肯定不会这样写,但是“loc_str”这个参数一般是上个页面给或服务接口给的,这样就不可控了,只要出现nil的情况就会闪退.

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:'NSConcreteMutableAttributedString initWithString:: nil value'

NSString *loc_str=nil;

NSMutableAttributedString *attriutedString = [[NSMutableAttributedString alloc] initWithString:loc_str];

在对象调用了自己没有的属性或方法是发生的闪退:如UIView 调用了 UIViewController 的方法时发生的闪退。

一般情况下不会发生类似问题,主要是在页面传值或者使用执行集中跳转逻辑的时候由于传参不合适或者是代码不规范造成问题。

*** -[UIWindow navigationController]: unrecognized selector sent to instance 0x101b07d60

if (! [viewController isKindOfClass:[UIViewController class]]) return;

[viewController.navigationController pushViewController:[self needJumpToVCName:vcClassName withParameter:parameter] animated:YES];

在构造 NSMutableAttributedString 或者 NSAttributedString 设置的属性值为 nil 导致闪退。

这个问题比较容易忽略,在实例化该对象是一定要小心、规范。

*** NSConcreteMutableAttributedString initWithString:: nil value

NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithString: originStr];

NSInternalInconsistencyException:内部不一致异常/内部矛盾异常

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',reason: 'Could not load NIB in bundle: 'NSBundle

NSArray*nib = [[NSBundle mainBundle] loadNibNamed:@"CellForOffers" owner:self options:nil];

loadNibNamed的xib不存在,出现NSInternalInconsistencyException错误

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',reason: 'attempt to delete row 13 from section 0 which only contains 12 rows before the update'

- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;

- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;

- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation API_AVAILABLE(ios(3.0));

- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath API_AVAILABLE(ios(5.0));

使用这4个方法要特别注意,它也数组一样容易越界,属于tableview的页面row越界

Invalid update: invalid number of rows in section 0.The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (3),plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted)and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).

这种情况一般是数据源变化了,操作的时候和之前的数据源不一致了,一般出现在一些业务场景异步处理数据源.比如消息,网络请求等

An instance 0x102471600 of class YYTextView was deallocated while key value observers were still registered with it. Current observation info:<NSKeyValueObservationInfo 0x174a20e60> ( <NSKeyValueObservance 0x174c59680: Observer: 0x174205ba0, Key path: contentSize, Options: <New: YES, Old: YES, Prior: NO> Context: 0x0, Property: 0x174c59740> )

@weakify(self)

[self.textInputView.textView addObserverBlockForKeyPath:@"contentSize" block:^(YYTextView *obj, id _Nullable oldVal, id _Nullable newVal) {

          @strongify(self)

           if (!self) return ;

}];


没写 [self.textInputView.textView removeObserverBlocks];ios11以下版本,会导致闪退

addObserverBlockForKeyPath却忘记移除,当这个类self回收了,再次触发监听就会出现找不到改对象而出现闪退.类似的监听还有通知、KVO都会导致闪退.需要注意的下在ios11以下都会闪退,亲测试ios11及以上不再会闪退,如果你的app需要兼容的系统版本是11及以上可以不需要写,但是为了研发规范和习惯,建议都写.

关于SIGSEGV

SIG 是信号名的通用前缀, SEGV 是 segmentation violation 的缩写

在 POSIX 兼容的平台上,SIGSEGV 是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号。SIGSEGV 的符号常量在头文件 signal.h 中定义。因为在不同平台上,信号数字可能变化,因此符号信号名被使用。通常,它是信号11。

闪退场景一:recorder deleteRecording 之前 先判断文件是否存在,否则会造成过度释放,解决方法:

if ([[NSFileManager defaultManager] fileExistsAtPath:self.recorder.url.path]) {

    if (![self.recorder deleteRecording])

            NSLog(@"Failed to delete %@", self.recorder.url);

}

闪退场景二: delegate = nil 。

将XXViewContrller设置为delegate时,当页面发生跳转时,XXViewController的对象会被释放,这是代码走到[_delegate callbackMethod],便出现crash。解决方法有二:1.将@property (nonatomic ,assign) id <BLELibDelegate>delegate; 中 assign关键字改为weak。 2.在XXViewController的delloc方法中添加:xxx.delegate = nil;

闪退场景三:键盘和使用了method swizzle将NSArray和NSMutableArray的objectAtIndex:等其他类进行数据安全导致的

不管行为轨迹是如何的,在哪个页面闪退,最后的本质应该都是一样的.ios11以下版本,我们目前报过来的基本是10.3.3和少量的10.3.2、10.3.1 由于没有真机,我们用模拟机模拟,如果不开启Xcode的Zombie Objects,不是毕现的,很少机率偶发的.调试的时候可以开启Xcode的Zombie Objects,并找到能弹出键盘的界面中,键盘显示的情况下 home app 进入后台,再单击app 图标 切换回前台时 发生crash.

处理:Safe文件设置为挂钩NSMutableArray方法编译器标志的位置 -fno-objc-arc 或者不使用方法交换的方式来做safe处理.

Methodmethod = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"),@selector(objectAtIndex:));Method method2 = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(safeObjectAtIndex:));method_exchangeImplementations(method, method2);

去调类似代码,让开发人员规范开发不依赖safe类.

好了就写到这里了,这就是我们项目中发现的一些问题,后面有新的问题再来补充,或大家有啥发现也可以来补充.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容