一. 背景
项目线上会偶现[YYKVStorage _dbClose]
的相关崩溃问题,崩溃堆栈如下:

二. 分析与治理
通过分析代码,这个崩溃发生在App
应用退出的时候,YYDiskCache
会调用_appWillBeTerminated
,将YYKVStorage
置为nil
,接着YYKVStorage
会调用dealloc
方法,最后调用[YYKVStorage _dbClose]
,在调用sqlite3_close
的时候出现了崩溃。

第一次治理
一开始从YYCache
的github
上面发现,线上也有反馈过类似的问题

因此采用了这里的治理方法,但上线后依然存在着result = sqlite3_close(_db);
崩溃问题。
第二次治理
因为崩溃是EXC_BAD_ACCESS (SIGSEGV)
类型,觉得可能是多线程导致的野指针问题。
从代码里面排查YYDiskCache
里面的YYKVStorage *_kv;
基本所有的操作都添加了锁操作。

只有极少数的读取操作没有添加锁操作。

因此调整了加锁的范围

但上线之后,也依然存在这个崩溃问题,显然也不是这里引起的。
第三次治理
因为前两次治理都没有解决这个问题,依据AI
和网上给的一些建议是替换sqlite3_close
函数,替换为sqlite3_close_v2
,并且去掉当关闭状态为result == SQLITE_BUSY || result == SQLITE_LOCKED
时候的重试逻辑。
该方案上线之后,也依然存在这个崩溃。
第四次治理
因为崩溃基本全是出现在iOS18
的系统,结合DeepSeek
以及一些文章给得的结论,有部分文章说iOS18
在应用程序退出时候,系统会对sqlite3
进行关闭回收操作,而YYCache
内部在应用即将退出UIApplicationWillTerminateNotification
的时候,会主动调用一次关闭,导致了关闭的时候,刚好系统关闭回收了sqlite3
,因此出现野指针。我们观察其他三方比如神策
里面sqlite3
的使用逻辑,并没有在应用即将退出的时候调用关闭sqlite3
的操作。
针对这个添加了降级方案,在iOS18
及以上的系统,当UIApplicationWillTerminateNotification
的时候,就不去调用_kv = nil
的操作,上线后,经验证,这个崩溃确实没出现。由于sqlite3
没有调用close
,但操作系统会帮忙回收,因此下次启动,打开也是正常的。