iOS开发的底线-崩溃

作为一个从事iOS开发的人,最初同事以及导师都叮嘱我写代码的时候一定要注意异常情况,底线就是不能写出任何有可能造成崩溃的代码。实际上,项目中有监测崩溃的工具,而且review的时候也会很严格检查,所以基本上那种有可能造成崩溃的代码基本都会在上线前修正。

但就在前些天,客户端发生了大面积一打开就闪退的问题,影响非常严重,后来查出是引入的其他部门的SDK没有进行类型判断而导致的崩溃。或许是开发人员的不小心,但我觉得更多的是平时没有养成习惯,没有考虑到对于一个拥有千万级别用户的应用来说,即使是万分之一的崩溃概率,也会有数千个用户崩溃,这在竞争激烈的互联网市场,是不能被容忍的。

我平时也没有过度重视,因为我总觉得理论上应该不可能崩溃,但是实际的场景太多,理论上不可能并不是百分百不可能,作为足够严谨的开发人员,必须守住自己的底线,不只是知道什么情况会造成崩溃,而是要养成一种编程习惯,所以特意分析了各种崩溃的情况。

数组越界

NSArray *firstNames = @[@"Roy", @"Mike", @"Jordan"];

NSString *name = firstNames[3];// 崩溃

崩溃信息:**** Terminating app due to uncaughtexception'NSRangeException', reason:'*** -[__NSArrayI objectAtIndexedSubscript:]: index 3 beyond bounds [0 .. 2]'*****

分析:可以看出当前数组的范围是0..2,当前下标超出了范围,即访问了未知的内存空间

注:除了数组可能越界之外,字符串也有可能越界,例如执行substringWithRange:消息时如果传递了过大的范围也会崩溃

字面量数组和字典插入nil值

数组

NSString *name;

NSArray *firstNames = @[@"Roy", @"Mike", @"Jordan", name];//崩溃

崩溃信息:**** Terminating app due to uncaughtexception'NSInvalidArgumentException', reason:'*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[3]'******

分析:通过崩溃信息可以很清楚看到是因为在字典初始化的时候插入了nil,实际上字面量语法是一种语法糖,本质是先创建了一个数组,然后把方括号内的所有对象添加到这个数组中

注:字面量语法让代码更加简洁,也能及时发现错误,但是最后创建的数组是不可变的

字典

NSNumber *jordanAge;

NSDictionary *ages = @{@"Roy":@22, @"Mike":@24, @"Jordan":jordanAge};//崩溃

崩溃信息:**** Terminating app due to uncaught exception'NSInvalidArgumentException',reason:'*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[2]'******

分析:同上面原因一样,都是插入了nil而导致的崩溃

注:当key为nil的时候插入也会崩溃

Unrecognized Selector

id person = @"person";

[person objectForKey:@"name"];//崩溃

崩溃信息:**** Terminating app due to uncaughtexception'NSInvalidArgumentException', reason:'-[__NSCFConstantString objectForKey:]: unrecognized selector sent to instance 0x1000010e8'******

分析:person对象无法执行objectForKey:消息,所以最后崩溃了

注:在用Objective-C语言编码时,我们会常常使用id类型更加便利地声明变量,但在执行消息前一定要确定它是否能响应,可使用respondsToSelector:检查。最常见的场景是调用代理方法,即使指定了代理对象,也不一定保证代理实现了相应方法(协议里还有可选实现的方法)

NaN崩溃

float number = NAN;

NSDictionary *dict = @{@"value": @(number)};

NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingSortedKeys error:nil];

崩溃信息:**** Terminating app due to uncaught exception'NSInvalidArgumentException',reason:'Invalid number value (NaN) in JSON write'******

分析:可以先来判断dict对象是否能被转换成JSON数据:BOOL isValidJSONObject = [NSJSONSerialization isValidJSONObject:dict];isValidJSONObject的结果是NO,也就是dict对象无法被转换为JSON数据,即NaN类型不能被用于JSON对象中

注:当进行不正常的数学运算时不只是会产生NaN类型,也有可能产生+inf类型,虽然并不会直接造成崩溃,但有可能在用它们进行其他操作的时候会有可能造成崩溃。通过isnan(x)和isinf(x)方法可以判断nan和inf类型

富文本初始化时字符串为空

NSString *text;

NSAttributedString *attributedText = [[NSAttributedString alloc]initWithString:text];//崩溃

崩溃信息:**** Terminating app due to uncaught exception'NSInvalidArgumentException',reason:'NSConcreteAttributedString initWithString:: nil value'******

分析:从崩溃信息中可以很明显看到是因为传入的变量值为nil而崩溃

注:构造NSMutableString时,如果传入的字符串为nil也会崩溃

创建未注册过的UITableViewCell

UITableViewCell*cell = [tableView    dequeueReusableCellWithIdentifier:@"reuseIdentifier"forIndexPath:indexPath]// 崩溃

服务器端出现问题而造成的崩溃

有一种情况,就是服务器端传递数据给客户端,客户端将其解析成模型对象,然后取模型里的值插入字面量语法构造的数组或者字典中。如果服务器端发生了问题,而客户端没有保护措施就会受到连累,当然实际上服务器端百分之九十九的概率是不可能发生问题的,所以很多人(包括我)也理所当然不会去特意多写一些多余的防御性代码。

我上面只是举了一个例子,一般我们都会和服务器端约定好数据格式以及其他细节,而且大多数时候都会做一些保护,但我真正想强调的是客户端不崩溃一定优于客户端依赖于服务器端而不崩溃,尽可能避免受到外界的影响

其他崩溃

在iOS 9.0之后NSNotificationCenter不会对一个dealloc的观察者发送消息,所以如果应用最低版本是9.0,其实也不必每次都去移除通知,但如果需要支持更低的版本,还是一定要移除通知,否则会崩溃

KVO不移除监听会导致奔溃,所以KVO的添加和移除必须成双成对

内存泄露,最著名的是代理和被代理对象循环引用

总结

以上都是我曾经遇到过的崩溃情况,当然还有很多我不知道的情况,毕竟技术是复杂的。我们或许可以使用一些工具来检查或者避免崩溃,但我还是想强调平时对待代码要更加严谨,对待负责的项目要更有责任感

上述代码的CrashDemos链接:github.com/iroyzhang/i…

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