Swift5 MacOS ImageResize内存问题

qlvxas9a  于 2023-06-21  发布在  Swift
关注(0)|答案(3)|浏览(157)

我是用Swift开发Mac OS App的新手。但我试着做了一个简单的ImageResizer应用程序。我必须调整大小50k图像。10小时后,内存增加到nealy 120GB。Swift也有垃圾收集器。为什么会增加记忆力?我给你看我的代码。

for i in 0..<paths.count {
    let path = paths[i]

    if let image = NSImage(contentsOf: path) {
        ...

        if self.resize(image: image, size: size, to: URL(fileURLWithPath: resizedImagePath)) {
            print("Image saved to \(resizedImagePath)")
            continue
        }
    }
}

func resize(image: NSImage, size: Int, to url: URL) -> Bool {
    if !image.isValid {
        print("invalid image")
        return false
    }

    guard let pixelsWide = image.representations.first?.pixelsWide else {
        return false
    }

    let factor: CGFloat = CGFloat(pixelsWide) / image.size.width

    var width: CGFloat = CGFloat(size)
    var height: CGFloat = CGFloat(size)
    if image.size.width > image.size.height {
        height = width * image.size.height / image.size.width
    } else {
        width = height * image.size.width / image.size.height
    }

    let rep = NSBitmapImageRep(bitmapDataPlanes: nil,
                               pixelsWide: Int(width),
                               pixelsHigh: Int(height),
                               bitsPerSample: 8,
                               samplesPerPixel: 4,
                               hasAlpha: true,
                               isPlanar: false,
                               colorSpaceName: .deviceRGB,
                               bytesPerRow: Int(width * 4),
                               bitsPerPixel: 32)
    rep?.size = NSSize(width: width / factor, height: height / factor)

    let ctx = NSGraphicsContext(bitmapImageRep: rep!)
    NSGraphicsContext.saveGraphicsState()
    NSGraphicsContext.current = ctx
    image.draw(in: NSMakeRect(0, 0, width / factor, height / factor))
    ctx?.flushGraphics()
    NSGraphicsContext.restoreGraphicsState()

    // Get NSData, and save it
    let data = rep?.representation(using: .png, properties: [:]) // properties as! [String : Any]) //
    do {
        try data?.write(to: url)
        return true
    }
    catch {
        return false
    }
}
8aqjt8rx

8aqjt8rx1#

你可以把你的整个代码放在一个autoreleasepool的循环中:
如果你写了一个循环,创建了很多临时对象。您可以在循环中使用自动释放池块,以便在下一次迭代之前释放这些对象。在循环中使用自动释放池块有助于减少应用程序的最大内存占用。

for i in paths.indices {
    autoreleasepool {
        // all your image resizing code goes here
    }
}
eblbsuwk

eblbsuwk2#

Swift使用ARC(自动引用计数),这意味着当对象的强引用数量达到零时,对象将被释放。我没有立即在代码中看到问题在哪里,但我怀疑在代码的其他地方一定有一些位置,您正在保存对图像的引用。

ffscu2ro

ffscu2ro3#

我遇到了一个类似的问题,在尝试了各种解决方案后,我尝试使用autoreleasepool,它没有帮助。过了一段时间,我发现了以下见解:
当使用UIImage和CGImage系统方法处理图像时,它会消耗大量内存。例如,一个尺寸约为4000 px x 3000 px的简单图像可以占用大约45 MB的内存。如果您的应用需要处理多个图像,或者您正在使用共享扩展,则应用可能会耗尽所有可用内存并崩溃。共享扩展的内存分配有限,只有120 MB,因此共享较大的图像(高度/宽度超过7000 px)很容易导致应用程序崩溃。这个问题甚至影响了iOS上一些最受欢迎的应用程序。
因此,除了避免处理大型图像文件或使用第三方工具之外,没有直接的解决方案。
解决这个问题的一个开源库是SDWebImage,可以在https://github.com/SDWebImage/SDWebImage上找到。将此库导入到项目中后,您可以使用它来处理图像,而无需担心内存泄漏或崩溃。
以下是如何在项目中使用SDWebImage调整图像大小或压缩图像的示例:

func resizeImage(atPath imagePath: URL) -> UIImage? {
    guard let image = UIImage(contentsOfFile: imagePath.path) else { return nil }
    
    guard let thumbnailData = SDImageIOCoder.shared.encodedData(with: image, format: .undefined, options: [.encodeMaxPixelSize: CGSize(width: 1920, height: 1920), .encodeCompressionQuality: 0.7]) else { return nil }
    
    return SDImageIOCoder.shared.decodedImage(with: thumbnailData)
}

相关问题