Проблема с размером изображения в памяти Swift5 для macOS

#ios #swift #memory-leaks

Вопрос:

Я новичок в разработке приложений для Mac OS с помощью Swift. Но я попытался создать простое приложение ImageResizer. Мне нужно изменить размер 50 тысяч изображений. Через 10 часов объем памяти увеличился до почти 120 ГБ. Я думал, что у 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
    }
}
 

Ответ №1:

Вы можете поместить весь свой код, который находится внутри вашего цикла, в пул авторелиза:

Если вы напишете цикл, который создает множество временных объектов. Вы можете использовать блок пула авторелиза внутри цикла, чтобы избавиться от этих объектов перед следующей итерацией. Использование блока пула авторелиза в цикле помогает уменьшить максимальный объем памяти приложения.

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

Ответ №2:

Swift использует ARC (Автоматический подсчет ссылок), что означает, что объекты будут освобождены, когда количество надежных ссылок на этот объект достигнет нуля. Я не сразу вижу в предоставленном коде, в чем проблема, но я подозреваю, что в другом месте вашего кода должно быть какое-то место, где вы держите ссылку на изображения.

Комментарии:

1. В нем нет другого кода, который мог бы увеличить объем памяти. Другой код-это просто получение пути назначения. И если я удалю предоставленный код, то это займет менее 1 секунды и не увеличит объем памяти.