1. 前言
递归是指一种通过重复将问题分解为同类的子问题而解决问题的方法。在程序中,通过函数直接或间接的调用自身来进行递归。[1]
我们在设计递归程序时需要一个或多个边界条件,用于退出递归。当未满足边界条件时调用函数自身,达到边界条件时退出递归。
2. 问题
在日常开发过程中,我们往往会使用递归处理一些逻辑。例如:在神策分析 iOS SDK 中,会使用递归逻辑进行埋点数据的上传。
简化流程如图 2-1 所示:
图 2-1 埋点数据上传的流程图
在程序中,如果函数无限调用自身,最终会导致栈溢出崩溃。通过流程图可以看出,退出递归的边界条件是所有数据上传成功或者某次数据上传失败,因此不会造成无限递归。
但是,仍有客户反馈此处递归发生了崩溃。
3. 排查经过
3.1. 推测原因
客户提供的部分堆栈信息如下:
... 35 SensorsAnalyticsSDK 0x0000000105d8e584 __39-[SAEventTracker flushRecordsWithSize:]_block_invoke_2 + 189828 (SAEventTracker.m:158) 36 SensorsAnalyticsSDK 0x0000000105d8e424 __39-[SAEventTracker flushRecordsWithSize:]_block_invoke + 189476 (SAEventTracker.m:161) 37 SensorsAnalyticsSDK 0x0000000105d8a4a8 -[SAEventFlush flushEventRecords:completion:] + 173224 (SAEventFlush.m:187) 38 SensorsAnalyticsSDK 0x0000000105d8e228 -[SAEventTracker flushRecordsWithSize:] + 188968 (SAEventTracker.m:147) 39 SensorsAnalyticsSDK 0x0000000105d8e584 __39-[SAEventTracker flushRecordsWithSize:]_block_invoke_2 + 189828 (SAEventTracker.m:158) 40 SensorsAnalyticsSDK 0x0000000105d8e424 __39-[SAEventTracker flushRecordsWithSize:]_block_invoke + 189476 (SAEventTracker.m:161) 41 SensorsAnalyticsSDK 0x0000000105d8a4a8 -[SAEventFlush flushEventRecords:completion:] + 173224 (SAEventFlush.m:187) 42 SensorsAnalyticsSDK 0x0000000105d8e228 -[SAEventTracker flushRecordsWithSize:]
|