通知服务扩展中UIGraphicsImageRenderer未返回及尺寸影响问题
问题描述
在通知服务扩展(非内容扩展)中接收推送时,尝试生成图片并添加到通讯录,但遇到UIGraphicsImageRenderer闭包无法完成返回的异常情况,具体表现如下:
异常场景1:CGContext绘图操作导致流程中断
当闭包中包含CGContext绘制矩形、UIImage.draw(at:)或UIImage.draw(in:)等操作时,闭包后的代码完全无法执行(如下示例中Line 2无日志输出),但该逻辑在主应用或通知内容扩展中可正常运行:
let mainImageRenderer = UIGraphicsImageRenderer(size: theSize) let mainImage = mainImageRenderer.image { context in context.cgContext.setFillColor(UIColor.clear.cgColor) context.fill(CGRect(origin: .zero, size: screenSize)) let rectangle = CGRect(x: 0, y: 0, width: 50, height: 50) context.cgContext.setFillColor(UIColor.red.cgColor) context.cgContext.setStrokeColor(UIColor.black.cgColor) context.cgContext.setLineWidth(10) context.cgContext.addRect(rectangle) context.cgContext.drawPath(using: .fillStroke) NSLog("Line 1 - This line logged") } NSLog("Line 2 - This line not logged and no image is created")
异常场景2:NSAttributedString.draw()可正常执行
若闭包中仅使用NSAttributedString.draw()操作,则整个流程可正常完成,闭包后的代码能执行,图片也能正常生成:
let textImage = textRenderer.image { context in context.cgContext.setFillColor(gray.cgColor) context.fill(CGRect(origin: .zero, size: textRectangleSize)) let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .center let attrs: [NSAttributedString.Key: Any] = [ .font: UIFont.systemFont(ofSize: 10), .paragraphStyle: paragraphStyle ] let attributedString = NSAttributedString(string: transcript, attributes: attrs) attributedString.draw(with: CGRect(x: 10, y: 10, width: textRectangleSize.width - 20, height:textRectangleSize.height - 20), options: .usesLineFragmentOrigin, context: nil) NSLog("Line 1 - This line logged") } NSLog("Line 2 - Now this line is logged and image is created")
尺寸影响行为
后续测试发现图片尺寸会影响结果:使用设备屏幕尺寸(如375×812)生成图片时流程中断,缩小尺寸至300×700则可正常运行:
private class func createBackdropImage() -> UIImage { let screenSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) let renderer = UIGraphicsImageRenderer(size: screenSize) let image = renderer.image { context in context.cgContext.setFillColor(UIColor.red.cgColor) context.fill(CGRect(origin: .zero, size: screenSize)) } NSLog("May or may not reach here depending upon size") return image }
疑问
- 为何UIKit在通知服务扩展中无法正常工作?为何
NSAttributedString.draw()不受影响? - 有无可替代
UIGraphicsImageRenderer的绘图API?需求为在UIImage中绘制矩形。 - 图片尺寸影响该行为的原因是什么?为何仅在通知服务扩展中出现此问题?
解答
1. 通知服务扩展中UIKit异常的原因
通知服务扩展属于轻量级后台扩展,系统对其资源限制(内存、CPU、执行时间)远严格于主应用和通知内容扩展:
- 使用
CGContext直接进行底层绘图时,大尺寸画布会瞬间拉高内存占用,触发系统内存预警,导致扩展被强制终止,因此闭包后的代码无法执行。 NSAttributedString.draw()内部做了高效的内存优化,文本渲染的内存占用远低于像素级图形绘制,能在扩展的资源限制内完成执行。
2. 替代UIGraphicsImageRenderer的绘图API
可以直接使用Core Graphics的传统绘图流程,手动管理上下文,更易控制内存:
func drawRectangle(size: CGSize, fillColor: UIColor, strokeColor: UIColor, lineWidth: CGFloat) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) defer { UIGraphicsEndImageContext() } guard let context = UIGraphicsGetCurrentContext() else { return nil } // 绘制背景 fillColor.setFill() context.fill(CGRect(origin: .zero, size: size)) // 绘制矩形 let rect = CGRect(x: 0, y: 0, width: 50, height: 50) strokeColor.setStroke() fillColor.setFill() context.setLineWidth(lineWidth) context.addRect(rect) context.drawPath(using: .fillStroke) return UIGraphicsGetImageFromCurrentImageContext() }
也可以使用CGBitmapContext手动创建位图上下文,精细控制内存分配,避免不必要的内存开销。
3. 尺寸影响行为的原因
- 图片尺寸越大,所需内存越高:比如375×812的Retina屏幕(@2x),实际像素为750×1624,每个像素按4字节计算,单张图片内存占用就超过4.5MB,加上绘图临时内存,极易突破通知服务扩展的内存限制(通常在10-15MB左右)。
- 通知服务扩展的内存阈值远低于主应用,主应用可使用更多后台内存,而扩展一旦超限就会被系统直接杀死,这就是仅在扩展中出现该问题的核心原因。
内容的提问来源于stack exchange,提问作者Piepants
相关产品推荐
相关产品推荐

