IOS开发入门之iOS开发中常见程序崩溃及解决方案
凌雪 2018-10-23 来源 :网络 阅读 1059 评论 0

摘要:本文将带你了解IOS开发入门iOS开发中常见程序崩溃及解决方案,希望本文对大家学IOS有所帮助。

本文将带你了解IOS开发入门iOS开发中常见程序崩溃及解决方案,希望本文对大家学IOS有所帮助。


 

程序崩溃经历

其实在很早之前就想写这篇文章了,一直拖到现在。

程序崩溃经历1   我们公司做的是股票软件,但集成的是第三方的静态库(我们公司和第三方公司合作,他们提供股票的服务,我们付钱)。平时开发测试的时候好好的,结果上线几天发现有崩溃的问题,其实责任大部分在我身上。   我的责任: 过分信赖文档,没进行容错处理,也就是没有对数据进行相应的判断处理。   下面附上代码,说明崩溃的原因

因第三方公司提供的数据错乱导致有时候创建字典的时候个别value为nil才导致的崩溃


   

<code><code>//宏

#define CStringToOcString(cstr) [NSString   stringWithCString:cstr encoding:GBK_ENCODE]

 

 

//将每组数据都保存起来

NSMutableArray *returnArray = [NSMutableArray   array];

for   (int i = 0; i   < recordM.count; i++) {

   Withdrawqry_entrust_record *record =   (Withdrawqry_entrust_record   *)alloca(sizeof(Withdrawqry_entrust_record));

   memset(record, 0x00,   sizeof(Withdrawqry_entrust_record));

   [[recordM objectAtIndex:i]   getValue:record];

    

 

   //崩溃的原因在创建字典的时候,有个别value为nil    (CStringToOcString)

 

   NSDictionary *param =     @{            

     @"batch_no" :   CStringToOcString(record->batch_no),// 委托批号

     @"entrust_no" :   CStringToOcString(record->entrust_no),// 委托编号

     @"entrust_type" : @(record->entrust_type),//委托类别  6 融资委托 7 融券委托   和entrust_bs结合形成融资买入,融资卖出,融券卖出,融券买入

     @"entrust_bs" : @(record->entrust_bs),// 买卖标志

     @"stock_account" : CStringToOcString(record->stock_account),//证券账号

     @"gdcode" :   CStringToOcString(record->gdcode),

     .....

     .....

     .....

      

                             };

   ```

 

 

 -    解决办法,在宏那里做了个判断,若果value为nil,直接赋值为@""

 

</code></code>

   

define   CStringToOcString(cstr) [NSString stringWithCString:cstr encoding:GBK_ENCODE]   ?

[NSString   stringWithCString:cstr encoding:GBK_ENCODE] :   @""


   

<code><code><code>

---

 

 

-   程序崩溃经历2

`不做过多的阐述,直接看代码`

 

</code></code></code>

   


   

<code><code><code><code>//服务器返回的日期格式为20160301

//我要将格式转换成2016-03-01

 

/** 委托日期 */

NSMutableString *dateStrM =   服务器返回的数据

 

[dateStrM   insertString:@"-" atIndex:4];

[dateStrM insertString:@"-" atIndex:7];

</code></code></code></code>

   


   

<code><code><code><code><code>就是上面的代码导致了上线的程序崩溃,搞的我在第二天紧急再上线了一个版本。

为何会崩溃呢?原因是服务器返回的数据错乱了,返回了0。这样字符串的长度就为1,而却插入下标为4的位置,程序必然会崩溃。后来在原本代码上加了一个判断,如下代码:

</code></code></code></code></code>

   

if   (dateStrM.length >= 8) {
[dateStrM   insertString:@"-" atIndex:4];
[dateStrM   insertString:@"-" atIndex:7];
}


   

<code><code><code><code><code>

---

醒悟

===

-   1、不要过分相信服务器返回的数据会永远的正确。

- 2、在对数据处理上,要进行容错处理,进行相应判断之后再处理数据,这是一个良好的编程习惯。

 

 

---

思考:如何防止存在潜在崩溃方法的崩溃

===

-   众所周知,Foundation框架里有非常多常用的方法有导致崩溃的潜在危险。对于一个已经将近竣工的项目,若起初没做容错处理又该怎么办?你总不会一行行代码去排查有没有做容错处理吧!--------   别逗逼了,老板催你明天就要上线了!

-   那有没有一种一劳永逸的方法?无需动原本的代码就可以解决潜在崩溃的问题呢?

---

 

解决方案

===

拦截存在潜在崩溃危险的方法,在拦截的方法里进行相应的处理,就可以防止方法的崩溃

 

步骤:

-   1、通过category给类添加方法用来替换掉原本存在潜在崩溃的方法。

- 2、利用runtime方法交换技术,将系统方法替换成我们给类添加的新方法。

- 3、利用异常的捕获来防止程序的崩溃,并且进行相应的处理。

  -   [如果对异常NSException不了解,可以点击查看NSException的介绍。](https://www.jianshu.com/p/05aad21e319e)

 

 

 

 

---

具体实现

===

创建一个工具类AvoidCrash,来处理方法的交换,获取会导致崩溃代码的具体位置,在控制台输出错误的信息......

- [代码中有正则表达式的知识点,不熟悉正则表达式的朋友们点我](https://www.jianshu.com/p/b25b05ef170d)

 

`AvoidCrash.h`

 

</code></code></code></code></code>

   

//
//   AvoidCrash.h
// AvoidCrash
//
// Created by mac on   16/9/21.
// Copyright ? 2016年 chenfanfang. All rights reserved.
//

import  

import  

//通知的名称,若要获取详细的崩溃信息,请监听此通知

define   AvoidCrashNotification   @"AvoidCrashNotification"

define   AvoidCrashDefaultReturnNil @"This framework default is to return   nil."

define   AvoidCrashDefaultIgnore @"This framework default is to ignore this   operation to avoid   crash."

@interface   AvoidCrash :   NSObject

/**

become   effective . You can call becomeEffective method in AppDelegate   didFinishLaunchingWithOptions开始生效.你可以在AppDelegate的didFinishLaunchingWithOptions方法中调用becomeEffective方法
*/

(void)becomeEffective;

(void)exchangeClassMethod:(Class)anClass   method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel;

(void)exchangeInstanceMethod:(Class)anClass   method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel;

(NSString   *)getMainCallStackSymbolMessageWithCallStackSymbolStr:(NSString   *)callStackSymbolStr;

(void)noteErrorWithException:(NSException   *)exception defaultToDo:(NSString   *)defaultToDo;

@end


   

<code><code><code><code><code>

`AvoidCrash.m`

</code></code></code></code></code>

   

//
//   AvoidCrash.m
// AvoidCrash
//
// Created by mac on   16/9/21.
// Copyright ? 2016年 chenfanfang. All rights   reserved.
//

import   "AvoidCrash.h"

//category

import   "NSArray+AvoidCrash.h"

import   "NSMutableArray+AvoidCrash.h"

import   "NSDictionary+AvoidCrash.h"

import   "NSMutableDictionary+AvoidCrash.h"

import   "NSString+AvoidCrash.h"

import   "NSMutableString+AvoidCrash.h"

define   AvoidCrashSeparator   @"================================================================"

define   AvoidCrashSeparatorWithFlag @"========================AvoidCrash   Log=========================="

define   key_errorName   @"errorName"

define   key_errorReason   @"errorReason"

define   key_errorPlace @"errorPlace"

define   key_defaultToDo   @"defaultToDo"

define   key_callStackSymbols   @"callStackSymbols"

define   key_exception   @"exception"

@implementation   AvoidCrash

/**

开始生效(进行方法的交换)
*/

(void)becomeEffective   {

static   dispatch_once_t onceToken;
dispatch_once(&onceToken,   ^{


   

<code><code><code><code><code><code>    [NSArray avoidCrashExchangeMethod];

  [NSMutableArray   avoidCrashExchangeMethod];

   

  [NSDictionary   avoidCrashExchangeMethod];

  [NSMutableDictionary   avoidCrashExchangeMethod];

   

  [NSString   avoidCrashExchangeMethod];

  [NSMutableString   avoidCrashExchangeMethod];

</code></code></code></code></code></code>

   

});
}

/**

类方法的交换@param   anClass 哪个类@param method1Sel 方法1@param method2Sel   方法2
*/(void)exchangeClassMethod:(Class)anClass   method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel {
Method   method1 = class_getClassMethod(anClass, method1Sel);
Method method2   = class_getClassMethod(anClass,   method2Sel);
method_exchangeImplementations(method1,   method2);
}

/**

对象方法的交换@param   anClass 哪个类@param method1Sel 方法1@param method2Sel   方法2
*/(void)exchangeInstanceMethod:(Class)anClass   method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel {
Method   method1 = class_getInstanceMethod(anClass, method1Sel);
Method   method2 = class_getInstanceMethod(anClass,   method2Sel);
method_exchangeImplementations(method1,   method2);
}

/**

获取堆栈主要崩溃精简化的信息<根据正则表达式匹配出来>@param   callStackSymbolStr 堆栈主要崩溃信息@return   堆栈主要崩溃精简化的信息
*/

(NSString   *)getMainCallStackSymbolMessageWithCallStackSymbolStr:(NSString   *)callStackSymbolStr   {

//mainCallStackSymbolMsg的格式为   +[类名 方法名] 或者 -[类名 方法名]
__block NSString *mainCallStackSymbolMsg =   nil;

//匹配出来的格式为   +[类名 方法名] 或者 -[类名 方法名]
NSString *regularExpStr =   @"[-\+]\[.+\]";

NSRegularExpression   *regularExp = [[NSRegularExpression alloc] initWithPattern:regularExpStr   options:NSRegularExpressionCaseInsensitive   error:nil];

[regularExp   enumerateMatchesInString:callStackSymbolStr options:NSMatchingReportProgress   range:NSMakeRange(0, callStackSymbolStr.length) usingBlock:^(NSTextCheckingResult   * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop)   {
if (result) {
mainCallStackSymbolMsg =   [callStackSymbolStr substringWithRange:result.range];
*stop =   YES;
}
}];


   

<code><code><code><code><code><code><code>return mainCallStackSymbolMsg;

</code></code></code></code></code></code></code>

   

}

/**

提示崩溃的信息(控制台输出、通知)@param   exception 捕获到的异常@param defaultToDo   这个框架里默认的做法
*/

(void)noteErrorWithException:(NSException   *)exception defaultToDo:(NSString *)defaultToDo   {

//堆栈数据
NSArray   *callStackSymbolsArr = [NSThread   callStackSymbols];

//获取在哪个类的哪个方法中实例化的数组   字符串格式 -[类名 方法名] 或者 +[类名 方法名]
NSString *mainCallStackSymbolMsg =   [AvoidCrash   getMainCallStackSymbolMessageWithCallStackSymbolStr:callStackSymbolsArr[2]];

if   (mainCallStackSymbolMsg == nil)   {

?

1

2

   

<code><code><code><code><code><code><code><code>    mainCallStackSymbolMsg = @"崩溃方法定位失败,请您查看函数调用栈来排查错误原因";

</code></code></code></code></code></code></code></code>

   

}

NSString   *errorName = exception.name;
NSString *errorReason =   exception.reason;
//errorReason 可能为 -[__NSCFConstantString   avoidCrashCharacterAtIndex:]: Range or index out of   bounds
//将avoidCrash去掉
errorReason = [errorReason   stringByReplacingOccurrencesOfString:@"avoidCrash"   withString:@""];

NSString   *errorPlace = [NSString stringWithFormat:@"Error   Place:%@",mainCallStackSymbolMsg];

NSString   *logErrorMessage = [NSString stringWithFormat:@"\n\n%@\n\n%@\n%@\n%@\n%@\n\n%@\n\n",AvoidCrashSeparatorWithFlag,   errorName, errorReason, errorPlace, defaultToDo,   AvoidCrashSeparator];
NSLog(@"%@",   logErrorMessage);

<code    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!

本文由 @凌雪 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程