首先,我想强调的是,根据Firebase Crashlytics,这个bug只涉及大约1% of the user base个.

我有一个包含许多heic图像的xcasset目录.

以下是加载和显示正常图像或模糊图像的代码.

// Original image
self.imageView.image = UIImage(named: "officeBackground")!

// Blurred image
self.imageView.image = AssetManager.shared.blurred(named: "officeBackground")

我使用一个管理器来缓存模糊的图像,这样我就不必每次显示它们时都重新生成它们.

final class AssetManager {
    static let shared = AssetManager()
    
    private var blurredBackground = [String: UIImage]()

    func blurred(named: String) -> UIImage {
        if let cachedImage = self.blurredBackground[from] {
            return cachedImage
        }
        let blurred = UIImage(named: named)!.blurred()!
        self.blurredBackground[from] = blurred
        return blurred
    }
}

最后是模糊代码

extension UIImage {
    func blurred() -> UIImage? {
        let ciimage: CIImage? = self.ciImage ?? CIImage(image: self)
        guard let input = ciimage else { return nil }
        let blurredImage = input.clampedToExtent()
            .applyingFilter("CIGaussianBlur", parameters: [kCIInputRadiusKey: 13])
            .cropped(to: input.extent)
        return UIImage(ciImage: blurredImage, scale: self.scale, orientation: .up)
    }
}

以下是我遇到的两种类型的车祸

  1. 具有CFAutorelease的CoreFoundation.Crashlytics提供了有关它的其他信息:
crash_info_entry_0:
*** CFAutorelease() called with NULL ***

CoreFoundation CFAutorelease stack trace crash

  1. 具有递归渲染的CoreImage.Crashlytics还提供了有关它的其他信息:
crash_info_entry_0: 
Cache Stats: count=14 size=100MB non-volatile=0B peakCount=28 peakSize=199MB peakNVSize=50MB

CoreImage recursive_render stack trace crash

我发现所有用户之间唯一的共同点是,他们在崩溃时有30-150个内存(根据Firebase,如果这个信息可靠的话?).

在这一点上,我真的很无知.CoreImage/CoreFoundation在处理内存中的CIImage时似乎存在一个bug.

奇怪的是,因为我使用AssetManager缓存模糊的图像,我知道在崩溃期间,用户已经在RAM中有了可用的缓存版本,但是当使用缓存的图像设置UIImageView时,它会因为内存不足而崩溃(?!).为什么系统甚至试图分配内存来执行此操作?

推荐答案

根据我的经验,直接使用CIImage创建的UIImage是非常不可靠和麻烦的.主要原因是CIImage实际上是一个位图图像,而receipt包含创建图像的指令.这取决于UIImage的消费者是否知道它是由CIImage支持的,并正确渲染它.UIImageView理论上是这样的,但我在这里看到了很多报告,所以这有点不可靠.正如ohglstr正确指出的那样,缓存UIImage并没有多大帮助,因为它仍然需要在每次使用时进行渲染.

我建议您使用CIContext自己渲染模糊图像并缓存结果.例如,您可以在AssetManager中这样做:

final class AssetManager {
    static let shared = AssetManager()
    
    private var blurredBackground = [String: UIImage]()
    private var ciContext: CIContext()

    func blurred(named name: String) -> UIImage {
        if let cachedImage = self.blurredBackground[name] {
            return cachedImage
        }

        let ciImage = UIImage(named: name)!.blurred()!
        let cgImage = self.ciContext.createCGImage(ciImage, from: ciImage.extent)!
        let blurred = UIImage(cgImage: cgImage)

        self.blurredBackground[name] = blurred
        return blurred
    }
}

Ios相关问答推荐

如何在SwiftUI中拥有基于操作系统版本的条件修饰符?

在SwiftUI中插入绘制重复形状

有没有办法将滚动视图RTL(阿拉伯语)与Ltr动画一起使用?

如何删除NavigationView中的默认透明标头?

无法从 Xcode 获取模拟器列表.请打开 Xcode 并try 直接从那里运行项目以解决剩余问题

如何包含 Xcode 子项目标头?

Xcode 14 弃用了位码 - 但为什么呢?

如何将私钥添加到分发证书?

如何使用 swift 显示 .svg 图像

为什么 Safari 在使用 iOS 6 设备进行远程调试时显示No Inspectable Applications?

无法验证客户端 3000

从 NSData 或 UIImage 中查找图像类型

在原生和 phonegap 之间挣扎,简单的应用需求

命令 /Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang 失败,退出代码为 1

如何关闭 iOS 键盘?

将视图控制器从一个情节提要移动或复制到另一个情节提要

如何使用 presentModalViewController 创建透明视图

从 Swift 转换为 Objective-c 的工具

在 SKScene 中设置按钮

didFailWithError: Error Domain=kCLErrorDomain Code=0 "操作无法完成.(kCLErrorDomain error 0.)"