You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UITableView DequeueReusableCell复用异常:重复创建单元且旧单元未释放

解决UITableView高可变Cell复用率低&内存泄漏问题

针对你遇到的高可变高度Cell(40-1000px)复用率不足、旧实例无法释放且内存占用高的问题,我结合SkiaSharp的使用场景给你几个具体的排查和解决方向:

  • 先搞定复用率低的核心问题
    首先检查Cell的注册与复用逻辑是否规范:确保你已经通过RegisterClassForCellReuse或RegisterNibForCellReuse完成了Cell的注册,且DequeueReusableCell时传入了正确的identifier——不要手动写if (cell == null) { new MyCell() }这种兜底逻辑,注册后的dequeue应该能稳定返回复用实例。
    另外,高度预估不准是导致系统频繁创建新Cell的常见原因:如果你的Cell用自动布局,一定要确保约束完整可解析;如果是手动计算高度,建议提前缓存每个indexPath对应的高度(比如用字典存储),并在GetHeightForRow中直接返回缓存值,同时合理设置EstimatedRowHeight——系统如果无法准确预估高度,会误以为现有复用Cell的尺寸不匹配当前行,从而创建新实例。

  • 强制清理SkiaSharp的非托管资源
    SKCanvasView依赖底层图形API(OpenGL/Metal),其相关对象(SKBitmap、SKPaint、SKCanvas等)都是非托管资源,.NET GC不会自动回收,必须手动释放:
    在Cell的PrepareForReuse方法里,一定要做这些操作:

    public override void PrepareForReuse()
    {
        base.PrepareForReuse();
        // 解除PaintSurface事件绑定,避免循环引用
        _canvasView.PaintSurface -= OnCanvasPaintSurface;
        // 释放自定义的SkiaSharp资源
        _myBitmap?.Dispose();
        _myPaint?.Dispose();
        _myBitmap = null;
        _myPaint = null;
        // 重置画布
        _canvasView.InvalidateSurface();
    }
    

    另外,当Cell即将从TableView中移除时(比如在WillMoveToSuperview方法中判断superview == null),也要重复一遍上述清理操作,确保资源被彻底释放。

  • 排查强引用导致的内存泄漏
    你提到用了WeakReference但没解决问题,大概率是存在未被发现的强引用链:

    • 检查Cell是否订阅了外部对象(比如ViewModel、全局事件)的事件,且没有在合适时机解除订阅——这些订阅会形成强引用,把Cell牢牢拉住。
    • 用内存分析工具(比如Xcode Instruments的Leaks,或者Visual Studio for Mac的内存探查器)跟踪Cell实例的引用链,找到持有Cell强引用的对象,针对性解除。
  • 优化复用池的适配逻辑
    由于你的Cell高度跨度极大(40到1000px),系统默认的复用池大小计算可能不准确,导致滚动时临时创建新Cell。你可以尝试:

    • 手动设置TableView.PreferredMaxLayoutWidth为你Cell的最大宽度,帮助系统更准确计算所需的复用实例数量。
    • 如果TableView的滚动方向是垂直,确保你的Cell的高度计算逻辑没有依赖动态变化的外部因素,避免系统频繁重新计算高度而放弃复用现有Cell。

按照这个思路一步步排查,应该能解决复用率低和内存泄漏的问题。

内容的提问来源于stack exchange,提问作者Michal Dobrodenka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:15:04