近期我们新做了一个需求,其中一个实现是需要在启动和使用这个功能时,从网络获取大量数据,然后将数据保存到数据库中。后续操作我们均从数据库获取。但是该需求上线后,Xcode中反馈了很多0xdead10cc
问题。因此本篇文章主要是介绍对0xdead10cc
的了解。
确定原因
首先先放出一张Xcode中Crash的堆栈信息
我们看到Crash信息中我们可以明确地看到Crash的原因:Termination Reason: Namespace RUNNINGBOARD, Code 0xdead10cc
。
我们先来看下这到底是一个什么错误。
1 |
|
通过上面的描述我们可以知道,0xdead10cc这个错误出现的原因是:在App被挂起期间,操作系统发现App依然持有一个文件锁或者数据库锁
而从堆栈信息中,我们可以看到在崩溃前的确是在执行数据库相关操作,那么这个Crash崩溃的原因应该是在挂起期间依然持有了数据库锁(进行数据库操作)
知识储备
首先我们应该明白什么是suspension状态,下面是官网截图
1 |
|
我们只需要重点看下这句话即可:When the user your app's UI, UIKit moves the associated scene to the background state and eventually to the suspended state.
当用户离开了你的APP,UIKit就会将相关的场景切换到后台状态,最终进入到挂起状态
对于当应用即进入后台时应该进行什么操作,苹果官方也给我们提供了详细的描述: Preparing Your UI to Run in the Background
我们都知道在我们进入后台时,App的代码是可以继续执行的,但是挂起态明显不可以,那么从后台状态到挂起态,我们有多久的时间可以继续执行任务呢?
这一点官方文档并没有明确的解释,只是提示我们要As Soon As Possible
,因此对于没有申请后台执行时间的APP来说,我们最好尽可能快的在APP进入后台时取消可能会继续持有的资源。当然如果APP需要可以申请额外的后台运行时间,但是时间也是有限的。
这里也找到了Finite-Length Tasks in background iOS swift供大家参考。
那么系统是否有提供通知,我们可以动态监听应用进入了挂起态呢?
在了解挂起态后,我们应该可以猜得到系统并不会给我们提供这个通知,因为在这个阶段App是不允许执行任何操作的,因此即使系统为开发者提供了这个通知,App也是无法监听并执行相应操作的。
这里找到了Execution States for a Swift iOS App这篇文章,其中介绍了Not Running
、Suspended
、Background
、Inactive
、Inactive
这几种状态有兴趣的可以了解下。
场景分析
经过上面的介绍,我们已经有了这方面的只是储备,那么在回到我们的crash上。下面是我对这次崩溃场景的猜测:App进入启动后,进入我们从接口获取数据保存到数据库的操作,对于数据较多的用户这里可能会持续时间较久,在这个过程中用户进入了后台,接口获取和保存数据库操作持续进行中,在某个接口请求完成后刚好到达临界时间,系统即将把App置为挂起状态。此时接口完成后的数据库操作,刚好导致了App持有数据库锁,因此系统将App强杀,并Report了上面的Crash
系统为何要这么做
因为我们的sqlite db是App Group共用(包括Extension)且文件是位于shared container,这就意味着文件会同时被App和Extension访问,假设App写操作在执行中途被suspend暂停,Extension唤醒后也对同一个App执行写操作,那么当App被重新唤醒继续之前的写操作时,写操作和db文件就会处于一个不可预知的状态,有可能造成写操作失败或者db文件损坏,所以系统选择了强杀App
解决方案
了解了问题出现的场景,我们就比较好去模拟场景,但是App从后台状态进入挂起态的时间是不确定的,因此在模拟该状态时时间点是无法确认的,因此尝试了多次后仍无法复现。
因此对于这个问题,我们只能从业务的角度,当App进入后台时我们将对应的数据库操作暂停,当应用再次回到前台时,通过判断之前是否有因进入后台而暂停的数据库操作,如果有,那么我们重新进行该次的接口+数据库写操作。
不过这样做副作用也很明显:在App进入后台之后,我们无法继续进行接口和网络请求了!这对于一些要求数据完全获取才可以展示的页面是十分不友好的。
期望
大家如果有什么比较好的复现这类问题的方法,可以留言提供感激不尽,如果有比价好的解决方案就更好了😄
参考文章
How can I tell if my app has suspended?
About the Background Execution Sequence
Springboard crashing when adding a lot of triggers to UNUserNotificationCenter