使用UIGraphicsImageRenderer生成图片时触发EXC_BAD_ACCESS崩溃
自定义窗口生成快照调用layer.render(in:)报EXC_BAD_ACCESS的修复方案
报错原因
EXC_I386_GPFLT类型的崩溃一般是访问了非法内存地址,该场景下常见触发原因如下:
- 整段渲染代码在后台线程执行,UIKit的layer渲染操作必须在主线程调用,后台执行会出现内存访问异常
- 传入的bounds参数非法,尺寸为0、包含负值或NaN,导致渲染器初始化异常,后续访问非法内存
- 渲染过程中layer所属的自定义窗口已经被释放,render调用时layer为野指针
修复方案
1. 主线程执行渲染逻辑
将整段快照生成代码包裹到主线程队列执行,代码示例:
DispatchQueue.main.async { let renderer = UIGraphicsImageRenderer(bounds: bounds, format: .init(for: traitCollection)) let targetImage = renderer.image { action in let context = action.cgContext layer.render(in: context) } // 处理生成的图片 }
2. 提前校验bounds合法性
渲染前先判断传入的尺寸是否合法,避免无效渲染:
guard bounds.width > 0, bounds.height > 0, !bounds.isInfinite else { return UIImage() // 非法尺寸兜底返回 } let renderer = UIGraphicsImageRenderer(bounds: bounds, format: .init(for: traitCollection)) return renderer.image { action in let context = action.cgContext layer.render(in: context) }
3. 持有目标对象避免提前释放
在渲染作用域内用临时变量强持有自定义窗口,确保渲染过程中对象不会被系统回收:
// 用临时强引用持有目标窗口 let strongReferenceWindow = yourCustomWindow let renderer = UIGraphicsImageRenderer(bounds: strongReferenceWindow.bounds, format: .init(for: strongReferenceWindow.traitCollection)) return renderer.image { action in strongReferenceWindow.layer.render(in: action.cgContext) }
4. 替代渲染方案
如果兼容iOS10及以上版本,可以用UIView的drawHierarchy方法替代layer直接渲染,稳定性更高:
let renderer = UIGraphicsImageRenderer(bounds: bounds, format: .init(for: traitCollection)) return renderer.image { action in yourCustomWindow.drawHierarchy(in: bounds, afterScreenUpdates: false) }
内容的提问来源于stack exchange,提问作者Hilal
相关产品推荐
相关产品推荐

