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

使用libwebp渲染WebP图片时遭遇EXC_BAD_ACCESS错误求助

在Swift中使用libwebp渲染WebP图片时解决EXC_BAD_ACCESS错误

我尝试在Swift中使用libwebp库在UIImageView中渲染WebP图片,但遇到了以下错误:

Thread 4: EXC_BAD_ACCESS (code=2, address=0x700008d57f38)

已确认libwebp库已正确链接,目标URL的WebP图片有效,以下是我实现的简化代码:

import UIKit
import libwebp

extension UIImageView {
    func renderWebPImage(from url: URL) {
        DispatchQueue.global().async {
            guard let webpData = try? Data(contentsOf: url) else {
                return
            }
            
            // Decode the WebP image data using libwebp
            guard let image = webpDecode(webpData) else {
                return
            }
            
            DispatchQueue.main.async {
                // Create a UIImage from the decoded data and assign it to the UIImageView
                self.image = UIImage(cgImage: image)
            }
        }
    }
    
    private func webpDecode(_ data: Data) -> CGImage? {
        // Decode the WebP image data using libwebp
        guard let webpDataPointer = data.withUnsafeBytes({ $0.baseAddress }),
              let webpDataLength = data.withUnsafeBytes({ $0.count }) as? Int else {
            return nil
        }
        
        // Create a WebPDecoderConfig object
        var config = WebPDecoderConfig()
        
        // Initialize the WebPDecoderConfig with default values
        WebPInitDecoderConfig(&config)
        
        // Decode the WebP image data and populate the WebPDecoderConfig object
        let result = withUnsafeMutablePointer(to: &config) { configPointer -> Int32 in
            return WebPDecode(webpDataPointer, webpDataLength, configPointer)
        }
        
        guard result == VP8_STATUS_OK,
              let image = config.output.u.RGBA.webpDecodedImage() else {
            return nil
        }
        
        return image
    }
}

extension WebPRGBABuffer {
    func webpDecodedImage() -> CGImage? {
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        
        guard let context = CGContext(
            data: self.rgba,
            width: Int(self.width),
            height: Int(self.height),
            bitsPerComponent: 8,
            bytesPerRow: Int(self.stride),
            space: colorSpace,
            bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue
        ) else {
            return nil
        }
        
        guard let cgImage = context.makeImage() else {
            return nil
        }
        
        return cgImage
    }
}

// Usage:
let imageView = UIImageView()
let url = URL(string: "https://example.com/image.webp")!

imageView.renderWebPImage(from: URL)

错误原因与修复方案

1. 内存泄漏与野指针问题

libwebp解码后会在config.output.u.RGBA.rgba分配堆内存,但你的代码未调用WebPFree释放该内存。当config变量超出作用域后,内存被回收,后续访问CGImage时会触发野指针错误。

修复:使用defer语句确保无论解码成功与否,都释放libwebp分配的内存;同时CGContext.makeImage()会复制数据,释放原内存不会影响生成的CGImage。

2. 错误的解码API调用

你使用的WebPDecode并非搭配WebPDecoderConfig的正确函数,应该使用WebPDecodeIntoConfig来填充传入的配置对象。

3. Data指针处理冗余

data.withUnsafeBytes的使用存在冗余,data.count本身就是Int类型,无需强制转换。

4. 使用代码中的低级错误

最后调用renderWebPImage时传入了类型URL而非实例url,会直接导致编译/运行错误。


修正后的完整代码

import UIKit
import libwebp

extension UIImageView {
    func renderWebPImage(from url: URL) {
        DispatchQueue.global().async { [weak self] in
            guard let self = self else { return }
            guard let webpData = try? Data(contentsOf: url) else {
                print("Failed to load WebP data from URL")
                return
            }
            
            guard let image = self.webpDecode(webpData) else {
                print("Failed to decode WebP image")
                return
            }
            
            DispatchQueue.main.async {
                self.image = UIImage(cgImage: image)
            }
        }
    }
    
    private func webpDecode(_ data: Data) -> CGImage? {
        let webpDataPointer = data.withUnsafeBytes { $0.baseAddress }
        let webpDataLength = data.count
        
        guard let pointer = webpDataPointer, webpDataLength > 0 else {
            return nil
        }
        
        var config = WebPDecoderConfig()
        WebPInitDecoderConfig(&config)
        
        // 使用正确的API填充解码配置
        let result = WebPDecodeIntoConfig(pointer, webpDataLength, &config)
        
        guard result == VP8_STATUS_OK else {
            WebPFreeConfig(&config)
            return nil
        }
        
        // 确保解码完成后释放libwebp分配的内存
        defer {
            WebPFree(config.output.u.RGBA.rgba)
        }
        
        guard let image = config.output.u.RGBA.webpDecodedImage() else {
            return nil
        }
        
        return image
    }
}

extension WebPRGBABuffer {
    func webpDecodedImage() -> CGImage? {
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        
        guard let context = CGContext(
            data: self.rgba,
            width: Int(self.width),
            height: Int(self.height),
            bitsPerComponent: 8,
            bytesPerRow: Int(self.stride),
            space: colorSpace,
            bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue
        ) else {
            return nil
        }
        
        // makeImage()会复制上下文数据,后续释放原内存不影响CGImage
        guard let cgImage = context.makeImage() else {
            return nil
        }
        
        return cgImage
    }
}

// 修正后的调用代码
let imageView = UIImageView()
guard let url = URL(string: "https://example.com/image.webp") else {
    fatalError("Invalid WebP image URL")
}
imageView.renderWebPImage(from: url)

额外注意点

  • 添加[weak self]避免UIImageView被提前释放时出现悬垂指针问题
  • 补充错误日志打印,方便排查加载/解码失败的原因
  • 生产环境中建议替换try? Data(contentsOf: url)为带错误处理的网络请求,避免同步阻塞线程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 16:05:13