prepareGeometryChange()引发的崩溃
最近在处理了Qt绘图中的崩溃问题,问题出在了QGraphicsSceneFindItemBspTreeVisitor::visit()函数中,其原因是由于软件代码编写不规范及Qt图形视图框架的bug导致,先做如下总结。
1. 场景
系统在一定配置下,记录一段时间,执行初始化全部操作,软件崩溃,其崩溃堆栈信息如下:
2. 分析
通过堆栈信息,可分析到此处有可能出现野指针的非法操作,但是由于堆栈信息不足,无法再做更详细的分析。
通过分析日志及代码流程梳理,复现出bug的崩溃场景,在PC端,有了更为详细的崩溃堆栈信息:
在上述堆栈信息中,发现异常操作出现在Qt图形视图框架的图元绘制过程中,我们再来看一下崩溃点附近的函数具体是在做什么。
崩溃点函数的功能是为了获取图元的顶层Item,此处操作代码逻辑简单,暂时无法分析出异常发生的原因。我们再来看看这个函数的调用点:
可以看出visit函数功能在于获取当前item列表的顶层Item去做一些处理,查看这些item列表,我们发现,这个列表中存在一些异常的item:
我们可以看到第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的帮助文档:
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索引,在绘图过程中引发崩溃。