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

UIImage扩展从字符串生成图片偶现EXC_BAD_ACCESS崩溃,如何排查?

排查UIImage扩展偶发EXC_BAD_ACCESS崩溃问题

我写了一个UIImage扩展,用来从图标字体的字符串生成图片,大部分时候运行正常,但偶尔会触发EXC_BAD_ACCESS导致崩溃,设备和模拟器都会出现,代码运行在主线程。想知道怎么排查根源,以及为什么只在少数场景下失败?

extension UIImage {

    static func image(withIconText text: String, font: UIFont, color: UIColor) -> UIImage {
        #if os(iOS)
            let scale = UIScreen.main.scale
        #else
            // Watch
            let scale = 2.0
        #endif
        
        let attributes = [
            NSAttributedString.Key.font: font,
            NSAttributedString.Key.foregroundColor: color,
        ]
        
        UIGraphicsBeginImageContextWithOptions(CGSize(width: font.pointSize, height: font.pointSize), false, scale)
        
        let textSize = (text as NSString).size(withAttributes: attributes)
        let origin = CGPoint(x: (font.pointSize - textSize.width) / 2, y: (font.pointSize - textSize.height) / 2)
        let rect = CGRect(origin: origin, size: textSize)

        // Crash: Thread 1: EXC_BAD_ACCESS
        text.draw(in: rect, withAttributes: attributes)
        
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return image ?? UIImage()
    }

}

崩溃根源排查与场景分析

1. 字体对象被提前释放(最可能原因)

EXC_BAD_ACCESS本质是访问了已释放的内存,大概率是传入的UIFont实例在draw(in:withAttributes:)执行前被回收了。少数场景触发是因为这个字体对象的生命周期仅在特定路径下没有被强引用持有——比如某些调用逻辑中字体是弱引用,或异步创建后未正确保留,刚好在绘图时引用计数降为0。

排查与修复:

  • 临时强引用字体,确保绘图全程持有:
    static func image(withIconText text: String, font: UIFont, color: UIColor) -> UIImage {
        // 强引用字体,避免提前释放
        let retainedFont = font
        #if os(iOS)
            let scale = UIScreen.main.scale
        #else
            let scale = 2.0
        #endif
        
        let attributes = [
            NSAttributedString.Key.font: retainedFont,
            NSAttributedString.Key.foregroundColor: color,
        ]
        // 后续代码不变
    }
    
  • 用Xcode的Zombies Instrument追踪:启动该模板复现崩溃,它会直接定位到被释放的对象类型(比如UIFont)和调用栈,确认是否为野指针访问。

2. 绘图上下文管理异常

即使在主线程调用,若存在未正确关闭的嵌套绘图上下文(比如之前的UIGraphicsBeginImageContextWithOptions未执行UIGraphicsEndImageContext),会导致当前上下文无效,进而触发draw操作崩溃。少数场景出现是因为这种上下文泄漏仅在特定调用路径中发生。

排查与修复:

  • 用defer确保上下文一定会被关闭,避免泄漏:
    UIGraphicsBeginImageContextWithOptions(CGSize(width: font.pointSize, height: font.pointSize), false, scale)
    defer { UIGraphicsEndImageContext() } // 无论代码分支如何,都会执行关闭
    
    // 中间绘图逻辑
    
    let image = UIGraphicsGetImageFromCurrentImageContext()
    return image ?? UIImage()
    
  • 检查所有调用该方法的场景,确认没有同时在其他地方操作绘图上下文的情况。

3. 特殊字符/字体文件异常

如果传入的text包含图标字体不支持的字符,或字体文件本身存在损坏(仅少数字符渲染出错),会触发底层渲染引擎崩溃。这种情况只会在使用特定字符时出现,所以表现为偶发。

排查与修复:

  • 复现崩溃时记录当时的text内容,单独测试这些字符是否会触发崩溃。
  • 替换为其他图标字体,验证崩溃是否消失,排除字体文件本身的问题。

为何仅少数场景失败?

  • 字体释放问题:只有当font对象的引用计数刚好在draw操作前降到0时才会崩溃,大部分场景下字体被其他强引用持有,所以正常运行。
  • 上下文异常:仅当存在上下文嵌套泄漏的特定调用路径才会触发,日常调用路径无此问题。
  • 特殊字符:只有使用字体无法正常渲染的字符时才会崩溃,常用图标字符无异常。

内容的提问来源于stack exchange,提问作者Andrei Herford

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 02:40:24