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

通知服务扩展中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
}

疑问

  1. 为何UIKit在通知服务扩展中无法正常工作?为何NSAttributedString.draw()不受影响?
  2. 有无可替代UIGraphicsImageRenderer的绘图API?需求为在UIImage中绘制矩形。
  3. 图片尺寸影响该行为的原因是什么?为何仅在通知服务扩展中出现此问题?

解答

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 04:24:33