prepareGeometryChange()引发的崩溃

最近在处理了Qt绘图中的崩溃问题,问题出在了QGraphicsSceneFindItemBspTreeVisitor::visit()函数中,其原因是由于软件代码编写不规范及Qt图形视图框架的bug导致,先做如下总结。

1. 场景
系统在一定配置下,记录一段时间,执行初始化全部操作,软件崩溃,其崩溃堆栈信息如下:

prepareGeometryChange()引发的崩溃

 2. 分析
通过堆栈信息,可分析到此处有可能出现野指针的非法操作,但是由于堆栈信息不足,无法再做更详细的分析。
通过分析日志及代码流程梳理,复现出bug的崩溃场景,在PC端,有了更为详细的崩溃堆栈信息:

prepareGeometryChange()引发的崩溃

在上述堆栈信息中,发现异常操作出现在Qt图形视图框架的图元绘制过程中,我们再来看一下崩溃点附近的函数具体是在做什么。

prepareGeometryChange()引发的崩溃

崩溃点函数的功能是为了获取图元的顶层Item,此处操作代码逻辑简单,暂时无法分析出异常发生的原因。我们再来看看这个函数的调用点:

prepareGeometryChange()引发的崩溃

可以看出visit函数功能在于获取当前item列表的顶层Item去做一些处理,查看这些item列表,我们发现,这个列表中存在一些异常的item:

prepareGeometryChange()引发的崩溃

我们可以看到第15项之后的item都是野值,所以才导致了系统崩溃问题。因此,可以分析出,此处的item列表处于异常状态,那么问题就在于是什么导致图元列表异常。
通过查询相关资料,发现这是一个Qt的bug,官方给出的解决方案是:
Resolution: Always call prepareGeometryChange() before changing the item's geometry. Otherwise, the scene's index will fall out of sync, and the behavior is undefined.
再来看看Qt的帮助文档:

prepareGeometryChange()引发的崩溃

If you want to change the item's bounding rectangle, you must first call prepareGeometryChange(). This notifies the scene of the imminent change, so that it can update its item geometry index; otherwise, the scene will be unaware of the item's new geometry, and the results are undefined (typically, rendering artifacts are left within the view).
3. 结论:
由于在自定义图元移动或者boundingrect变化时,没有提前调用prepareGeometryChange(),导致场景内可能存在无效的item索引,在绘图过程中引发崩溃。