0

I have an array of images and need to merge these images with different blending options. I found, that during this process memory is not released each step of cycle. Even if there are no references to images which were created. My app crashes during this process because I have many images to merge. Is there any workaround or trick how I can do it without memory increasing? It is possible to reproduce with the following code:

import UIKit
import CoreImage

class ViewController: UIViewController {
    var images =
            [
                UIImage.init(color: UIColor.green, size: CGSize(width: 2048, height: 1152))!,
                UIImage.init(color: UIColor.green, size: CGSize(width: 2048, height: 1152))!,
                UIImage.init(color: UIColor.green, size: CGSize(width: 2048, height: 1152))!,
                UIImage.init(color: UIColor.green, size: CGSize(width: 2048, height: 1152))!,
                UIImage.init(color: UIColor.green, size: CGSize(width: 2048, height: 1152))!,
            ]
    override func viewDidLoad() {
        super.viewDidLoad()
        var result = UIImage.init(color: UIColor.blue, size: CGSize(width: 2048, height: 1152))!
        for image in images {
            //each step reference to previous result released, but memory is growing
            result = merge(result, image)
            sleep(1)
        }
    }

    public func merge(_ back: UIImage, _ top: UIImage) -> UIImage {
        var size = CGSize(width: 2048, height: 1152)
        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
        let rec = CGRect(x: 0, y: 0, width: 2048, height: 1152)
        back.draw(in: rec)
        //multiply is just for example, it can be different blending option
        top.draw(in: rec, blendMode: .multiply, alpha: 1)
        var newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return newImage
    }
}

extension UIImage {
    public convenience init?(color: UIColor, size: CGSize) {
        let rect = CGRect(origin: .zero, size: size)
        UIGraphicsBeginImageContextWithOptions(rect.size, true, 0)
        color.setFill()
        UIRectFill(rect)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        guard let cgImage = image?.cgImage else {
            return nil
        }
        self.init(cgImage: cgImage, scale: UIScreen.main.scale, orientation: Orientation.up)
    }
}

Array with dummy images and cycle. I added sleep to see it better in Instruments (without sleep it has the same result) Memory is growing

As you can see each second memory is increasing. Any way to keep memory on same level each step? Many thanks!

Artem
  • 364
  • 2
  • 18

1 Answers1

1

You can wrap merging into autoreleasepool scope:

for image in images {
    autoreleasepool {
        result = merge(result, image)
    }
    sleep(1)
}

UIKit adds objects it produces into the autoreleasepool so that they are released at the end of the run loop cycle.

Eugene Dudnyk
  • 5,553
  • 1
  • 23
  • 48