From 9ce300ddb9165193900350f093158b1299575d41 Mon Sep 17 00:00:00 2001 From: iven Date: Wed, 3 Jun 2026 01:06:34 +0800 Subject: [PATCH] =?UTF-8?q?fix(app):=20=E4=BF=AE=E5=A4=8D=E7=AC=94?= =?UTF-8?q?=E7=94=BB=E7=BC=93=E5=AD=98=20use-after-dispose=20=E2=80=94=20?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=A2=9E=E9=87=8F=E5=90=88=E6=88=90=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E6=8F=90=E5=89=8D=20dispose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _compositeIncremental 中不再 dispose strokeImage,因为 _cache 持有同一引用 - 提前 dispose 导致 syncStrokes/clear/dispose 时 double-dispose(use-after-free) - 单笔画 image 生命周期由缓存统一管理:移除/清除/销毁时释放 - 更新 _rebuildComposite 注释,移除过时说明 审计 ID: 8b-R01 --- app/lib/features/editor/widgets/stroke_cache.dart | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/app/lib/features/editor/widgets/stroke_cache.dart b/app/lib/features/editor/widgets/stroke_cache.dart index 87cee88..45ee575 100644 --- a/app/lib/features/editor/widgets/stroke_cache.dart +++ b/app/lib/features/editor/widgets/stroke_cache.dart @@ -232,14 +232,10 @@ class StrokeRasterCache { _canvasSize.height.toInt().clamp(1, 4096), ); - // 单笔画图像已合成,释放以节省 GPU 内存 - strokeImage.dispose(); - // 注意:从 _cache 中移除该条目的 image(保留 stroke 引用以备重建) - // 不,缓存保留 image 引用以便撤销时重建。增量合成时释放 strokeImage - // 但 _cache 仍持有引用,所以需要用另一个方式 - // 实际上增量合成后可以释放单笔画图像——合成图已包含其内容 - // 但撤销时需要重建,需要原始数据。保留 Stroke 数据,释放 image。 - // 如果后续撤销,syncStrokes 会重新光栅化。 + // 注意:不在此处 dispose strokeImage! + // _cache 和此方法持有同一 image 引用,提前 dispose 会导致 + // syncStrokes/clear/dispose 时 use-after-dispose(审计 ID: 8b-R01)。 + // 单笔画 image 由缓存统一管理生命周期(移除/清除/销毁时释放)。 _layerVersion++; } @@ -265,8 +261,7 @@ class StrokeRasterCache { ); // 按笔画顺序重新绘制所有单笔画 - // 注意:增量合成时已释放了单笔画 image,这里需要重新光栅化 - // 所以全量重建时,直接用 stroke 数据重绘路径(不依赖缓存的 image) + // 直接从 stroke 数据重绘路径,确保与增量合成结果一致 for (final entry in _cache.values) { final stroke = entry.stroke; final outlinePoints = pointsToOutline(