前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >iOS Crash不崩溃

iOS Crash不崩溃

作者头像
用户2814378
发布于 2022-11-07 07:12:02
发布于 2022-11-07 07:12:02
2.2K00
代码可运行
举报
文章被收录于专栏:高科技宣传高科技宣传
运行总次数:0
代码可运行

用户在使用App的过程中,经常遇到闪退的情况,体验不太好,本文尝试探索引发闪退的原因,以及在遇到crash的情况下,尽可能的保持程序运行,并及时上报错误。

一、crash类型

1.OC层面的crash

1.1 普通类型

  • NSInvalidArgumentException:非法参数异常,传入非法参数导致异常,nil参数比较常见。
  • NSRangeException:下标越界导致的异常。
  • NSGenericException: foreach的循环当中修改元素导致的异常。

1.2 KVO

KVO Crash常见原因:

  • 移除未注册的观察者
  • 重复移除观察者
  • 添加了观察者但是没有实现-observeValueForKeyPath:ofObject:change:context:方法
  • 添加移除keypath=nil
  • 添加移除observer=nil

1.3 unrecognized selector sent to instance

  • 对象接收到未知的消息,即下图中消息未能处理的情况。

2.Signal层面的crash

除了OC层面的异常捕获之外,很多内存错误、访问错误的地址产生的crash则需要利用unix标准的signal机制,注册SIGABRT, SIGBUS, SIGSEGV等信号发生时的处理函数。该函数中我们可以输出栈信息,版本信息等其他一切我们所想要的。

  • SIGKILL:用来立即结束程序的运行的信号。
  • SIGSEGV:试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据。
  • SIGABRT:调用abort函数生成的信号。
  • SIGTRAP:由断点指令或其它trap指令产生。
  • SIGBUS:非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。

二、存在问题

程序闪退,用户体验不好

三、监听crash

1.任凭程序闪退并上报

1.1 NSSetUncaughtExceptionHandler 捕获OC层面的crash

参考文章

(1)AppDelegate中添加捕获监听

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
  return YES;
  }

(2)解析堆栈信息并上报

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void UncaughtExceptionHandler(NSException *exception) {
  /**
   *  获取异常崩溃信息
   */
  NSArray *callStack = [exception callStackSymbols];
  NSString *reason = [exception reason];
  NSString *name = [exception name];
}

1.2 Appdelegate中注册SIGABRT, SIGBUS, SIGSEGV等信号发生时的处理函数,处理Signal层面的crash。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void InstallSignalHandler(void)
{
  signal(SIGHUP, SignalExceptionHandler);
  signal(SIGINT, SignalExceptionHandler);
  signal(SIGQUIT, SignalExceptionHandler);
  
  signal(SIGABRT, SignalExceptionHandler);
  signal(SIGILL, SignalExceptionHandler);
  signal(SIGSEGV, SignalExceptionHandler);
  signal(SIGFPE, SignalExceptionHandler);
  signal(SIGBUS, SignalExceptionHandler);
  signal(SIGPIPE, SignalExceptionHandler);
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void SignalExceptionHandler(int signal)
{
  
  NSMutableString *mstr = [[NSMutableString alloc] init];
  [mstr appendString:@"Stack:\n"];
  void* callstack[128];
  int i, frames = backtrace(callstack, 128);
  char** strs = backtrace_symbols(callstack, frames);
  for (i = 0; i <frames; ++i) {
      [mstr appendFormat:@"%s\n", strs[i]];
  }
  [SignalHandler saveCreash:mstr];

}

2.Crash自动修复+捕获上报

2.1 针对普通类型Crash的处理机制

hook相关的方法,增加保护机制。 以NSArray越界为例,hook objectAtIndex方法,在方法中捕获越界异常,并在最后返回一个nil对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[self exchangeInstanceMethod:__NSArrayI method1Sel:@selector(objectAtIndex:) method2Sel:@selector(avoidCrashObjectAtIndex:)];
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (id)avoidCrashObjectAtIndex:(NSUInteger)index {
  id object = nil;
  
  @try {
      object = [self avoidCrashObjectAtIndex:index];
  }
  @catch (NSException *exception) {
     //捕获异常,根据exception打印出堆栈信息,同时也避免了程序崩溃
  }
  @finally {
      return object;
  }
}

注意:使用方法进行捕获异常之后,第三方工具将不会搜集到崩溃信息并上报,需要在catch中手动上报。

2.2 针对KVO Crash的处理机制

新建一个对象,用来记录target,observer,context,keypath等,每添加一个监听,增加一个对象,用一个数组维护。添加和删除的时候做判断,同时hook dealloc函数,dealloc的同时移除我的观察者和我观察的对象。dealloc时遍历数组,数组中不应该存在对象,如果存在对象,应该抛出异常并接收,提示用户KVO的释放存在问题。

  • 移除未注册的观察者:在移除A对象的观察者时,先判断数组中是否有A对象的观察者,如果有,再移除。
  • 重复移除观察者:同上
  • 添加了观察者但是没有实现-observeValueForKeyPath:ofObject:change:context:方法:hook observeValueForKeyPath方法,增加try-catch即可。
  • 添加移除keypath=nil:hook添加移除观察者的方法,在新方法中过滤keypath=nil的情况。
  • 添加移除observer=nil:hook添加移除观察者的方法,在新方法中过滤observer=nil的情况。

注意:使用方法进行捕获异常之后,第三方工具将不会搜集到崩溃信息并上报,需要在catch中手动上报。

2.3 针对unrecognized selector解决方案

通常,当我们不能确定一个对象是否能接收某个消息时,会先调用respondsToSelector:来判断一下。如下代码所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if ([self respondsToSelector:@selector(method)]) {
[self performSelector:@selector(method)];
}

当一个对象无法接收某一消息时,就会启动所谓”消息转发(message forwarding)“机制,通过这一机制,我们可以告诉对象如何处理未知的消息。默认情况下,对象接收到未知的消息,会导致程序崩溃。

上图可以看出,在一个函数找不到时,Objective-C提供了三种方式去补救:

1、调用resolveInstanceMethod给个机会让类添加这个实现这个函数

2、调用forwardingTargetForSelector让别的对象去执行这个函数

3、调用methodSignatureForSelector(函数符号制造器)和forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。

如果都不中,调用doesNotRecognizeSelector抛出异常。

- (void)forwardInvocation:(NSInvocation *)anInvocation

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

方法一:hook上述两个方法,在methodSignatureForSelector中返回有效的NSMethodSignature,在forwardInvocation中添加try-catch即可,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 [self exchangeInstanceMethod:[self class] method1Sel:@selector(methodSignatureForSelector:) method2Sel:@selector(avoidCrashMethodSignatureForSelector:)];
 [self exchangeInstanceMethod:[self class] method1Sel:@selector(forwardInvocation:) method2Sel:@selector(avoidCrashForwardInvocation:)];
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (NSMethodSignature *)avoidCrashMethodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *ms = [self avoidCrashMethodSignatureForSelector:aSelector];
    if ([self respondsToSelector:aSelector] || ms){
        return ms;
    }
    else{
        return [SafeProxy instanceMethodSignatureForSelector:@selector(safe_crashLog)];
    }
}

- (void)avoidCrashForwardInvocation:(NSInvocation *)anInvocation {
    
    @try {
        [self avoidCrashForwardInvocation:anInvocation];
        
    } @catch (NSException *exception) {
      //捕获异常,根据exception打印出堆栈信息,同时也避免了程序崩溃
      //上报
    } @finally {
        
    }
}

方法二:直接hook doesNotRecognizeSelector也可实现,doesNotRecognizeSelector起到抛出异常的作用,自己增加try-catch进行捕获即可,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[self exchangeInstanceMethod:[self class] method1Sel:@selector(doesNotRecognizeSelector:) method2Sel:@selector(avoidCrashDoesNotRecognizeSelector:)];
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- (void)avoidCrashDoesNotRecognizeSelector:(SEL)aSelector{
    @try {
        [self avoidCrashDoesNotRecognizeSelector:aSelector];
        
    } @catch (NSException *exception) {
       //捕获异常,根据exception打印出堆栈信息,同时也避免了程序崩溃
       //上报
    } @finally {
        
    }
}

效果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
NSInvalidArgumentException
*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[1]
Error Place:-[ViewController NSArray_Test_InstanceArray]
AvoidCrash default is to remove nil object and instance a array.

打印出了堆栈信息,同时避免了程序崩溃。

注意:使用方法进行捕获异常之后,第三方工具将不会搜集到崩溃信息并上报,需要在catch中手动上报。

2.4 针对野指针的处理机制

模仿Xcode的zombie机制:

1.Swizzle原有allocWithZone方法,添加野指针防护标记。

2.Swizzle原有dealloc方法,如果有野指针防护标记,调用 objc_destructInstance方法,修改实例isa使其指向zombieObject,保存原始 类名,以便上报使用。

3.Swizzle消息转发机制forwardingTargetForSelector方法,处理所 有原始类originObject的方法,收集错误信息并上报。

4.及时释放zombieObject。

注: objc_destructInstance会释放与实例相关联的引用,但是并不释放该实例的内存。

参考文章

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=j8ti7e982xdo

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-10-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
用 Python 告诉你!最近 24 小时内朋友圈发生了什么
之前有一篇 文章 使用 Airtest 实现自动看新闻薅羊毛这一操作,其实 Airtest 功能远不止此。
小小詹同学
2019/07/12
1.1K0
用 Python 告诉你!最近 24 小时内朋友圈发生了什么
薅羊毛 | Python 自动化带你轻松赚钱
最近,有一个朋友告诉我,她在某平台上购买了一部手机,收到货之后发现商品质量挺好的,价格也不贵。
数据森麟
2019/10/15
1.1K0
薅羊毛 | Python 自动化带你轻松赚钱
自动化篇 | 你想要的闲鱼日常操作,Python 给你实现了
前篇文章讲了如何在 iOS 端使用 Airtest 实现自动化的操作,本篇将继续带大家做 iOS 端的自动化项目。
AirPython
2020/03/23
3.6K2
自动化篇 | 你想要的闲鱼日常操作,Python 给你实现了
薅羊毛 | 让Python每天帮你薅一个早餐钱
以今日头条极速版为首,包含趣头条、东方头条、全名小视频在内的 App 都有看新闻、视频送金币的活动,当金币达到一定量后,就可以提现到微信、支付包。
sergiojune
2019/05/15
9310
薅羊毛 | 让Python每天帮你薅一个早餐钱
Python实现自动抢红包功能
可能有人每天都忙碌于各类微信群中,专注抢红包。那是否可以利用 Python 实现自动抢红包呢?
axiomxs
2021/11/26
2.2K0
Python实现自动抢红包功能
带你用 Python 实现自动化群控设备
群控,相信大部分人都不会陌生!印象里是一台电脑控制多台设备完成一系列的操作,更多的人喜欢把它和 Hui 产绑定在一起!
AirPython
2020/08/15
1.2K0
聊聊 Airtest 自动化工具
很多读者看过之前的文章,发现我使用最多的是一款 Airtest 的自动化测试框架。
测试开发社区
2019/10/31
1.7K0
聊聊 Airtest 自动化工具
GitHub 超级火:超全开源爬虫工具箱!
最近国内一位开发者在 GitHub 上开源了个集众多数据源于一身的爬虫工具箱——InfoSpider,一不小心就火了!!!
我被狗咬了
2020/11/04
7460
GitHub 超级火:超全开源爬虫工具箱!
Python爬取某宝商品数据案例:100页的价格、购买人数等数据
随着互联网时代的到来,人们更加倾向于互联网购物,某宝又是电商行业的巨头,在某宝平台中有很多商家数据,今天带大家使用python+selenium工具获取这些公开的。
松鼠爱吃饼干
2020/09/15
1.6K0
Python爬取某宝商品数据案例:100页的价格、购买人数等数据
谁偷偷删了你的微信?别慌!Python 帮你都揪出来了
不知道你有没有经历过,想联系一位很长时间没有联系的朋友,发现对方很早以前已经把你删除了,而你还一无所知。
小小詹同学
2019/08/01
9700
谁偷偷删了你的微信?别慌!Python 帮你都揪出来了
airtest测试用例_Airtest 常用方法
auto_setup(__file__,devices=[“Android://127.0.0.1:5037/SJE5T17B17”])#方法二:用connect_device()方法连接设备
全栈程序员站长
2022/11/15
1.7K0
天猫商品详情采集上架拼多多店铺(淘宝商品详情上架至拼多多店铺)采集接口代码展示
业务场景:大家有探讨稳定采集天猫商品详情搬货至拼多多店铺,采集数据包括:获取商品列表和商品详情页面数据,包括:价格,主图、价格、标题,sku,sku描述等页面上有的数据接口完整解决方案。这个引起了我技术挑战的兴趣。目前,自己做了压测,QPS高、出滑块概率极低,API整体稳定,可满足业务场景的性能需求。
wx19970108018
2023/03/06
7060
天猫商品详情采集上架拼多多店铺(淘宝商品详情上架至拼多多店铺)采集接口代码展示
Python爬虫学习之天猫商品数据爬虫
天猫商品数据爬虫使用教程 下载chrome浏览器 查看chrome浏览器的版本号,对应版本号的chromedriver驱动 pip安装下列包 pip install selenium pip install pyquery 登录微博,并通过微博绑定淘宝账号密码 在main中填写chromedriver的绝对路径 在main中填写微博账号密码 #改成你的chromedriver的完整路径地址 chromedriver_path = "/Users/bird/Desktop/chromedriv
python学习教程
2019/12/27
1.1K0
用 Python 在朋友圈中游遍全球
十一长假,相信大部分的朋友这会应该是在全国各地浪或者是在浪的路上,朋友圈成为你们表演的场所。
小小詹同学
2019/10/10
9700
用 Python 在朋友圈中游遍全球
淘宝数据分析实战:美食霸榜销量No.1的竟然是....
想必大家都喜欢囤货吧,小编购物车里全是加购的零食,闲来无事,就顺手爬了淘宝搜索美食出来的商品信息,简单做了个分析,借此案例给大家学习参考。
1480
2020/02/19
4850
淘宝商品sku接口,商品详情数据,商品销量数据,商品优惠券,行业分析,竞品分析,品牌监控接口代码展示
业务场景:作为全球最大的 B2C 电子商务平台之一,淘宝平台提供了丰富的商品资源,吸引了大量的全球买家和卖家。为了方便开发者接入淘宝平台,淘宝平台提供了丰富的 API 接口,其中商品详情接口是非常重要的一部分。大家有探讨稳定采集淘宝整站实时商品详情评论数据接口,通过该接口开发者可以更好地了解商品的情况,商品详情详细信息查询,数据参数包括:获取商品列表主图、价格、标题,sku,商品评论日期,评论内容,评论图片,买家昵称,追评内容,商品属性,追评属性图片等页面上有的数据完整解决方案帮助买家更准确地进行商品选购。这个引起了我技术挑战的兴趣。目前,自己做了压测,QPS 高、出滑块概率极低,API 整体稳定,可满足业务场景的性能需求。
wx19970108018
2023/03/23
1.1K0
淘宝商品sku接口,商品详情数据,商品销量数据,商品优惠券,行业分析,竞品分析,品牌监控接口代码展示
Python教你挑选礼物
又到了一年一度的教师节,每次教师节大家都会烦恼不知道送什么礼物?尤其是对于理工男来说,更是一个让人头大的问题。我今天就和大家分享一个用Python爬取商品信息的项目,希望可以给大家选礼物时提供一个参考。
stormwen
2019/09/12
1.2K0
Python教你挑选礼物
数据可视化|用Python实现手机抓包,获取当当图书差评数据!
本次学习了手机抓包的相关知识,了解了Charles-mitmproxy-Appium的基本使用,通过对当当图书评论的爬取,得以实践。
龙哥
2019/07/30
1.1K0
数据可视化|用Python实现手机抓包,获取当当图书差评数据!
从登陆到爬取:Python反反爬获取某宝成千上万条公开商业数据
chrom浏览器的驱动器下载地址:http://npm.taobao.org/mirrors/chromedriver/
荣仔_最靓的仔
2021/02/02
1.2K0
从登陆到爬取:Python反反爬获取某宝成千上万条公开商业数据
淘宝商品信息定向爬虫
最近在学习数据分析的时候,提前学习了爬虫,其实在很早之前就学习过Python了,但是后来学习Android了,没有将Python很好的实践。但是学习了爬虫之后,我觉得自己写爬虫收集数据,放在自己的服务器上,然后再用自己的App展示出来,就不用再苦于没有数据了。但是,要想写出好的爬虫,也是一件不容易的事情,需要多加练习。
GitOPEN
2019/01/29
1.6K0
淘宝商品信息定向爬虫
推荐阅读
相关推荐
用 Python 告诉你!最近 24 小时内朋友圈发生了什么
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验