2

I am writing a perceptual matching program (to practically learn Swift). I have the following challenge:

I am having a class to convert a CGImage to a bitmap to subsequently read the individual pixels (see How do I load and edit a bitmap file at the pixel level in Swift for iOS?)

class Bitmap {       

let width: Int
let height: Int
let context: CGContextRef

init(img: CGImage) {

    // Set image width, height
    width = CGImageGetWidth(img)
    height = CGImageGetHeight(img)

    // Declare the number of bytes per row. Each pixel in the bitmap in this
    // example is represented by 4 bytes; 8 bits each of red, green, blue, and alpha.
    let bitmapBytesPerRow = width * 4
    // Use the generic RGB color space.
    let colorSpace = CGColorSpaceCreateDeviceRGB()

    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue)

    // Create the bitmap context. 
    context = CGBitmapContextCreate(nil, width, height, 8, bitmapBytesPerRow, colorSpace, bitmapInfo.rawValue)!

    // draw the image onto the context
    let rect = CGRect(x: 0, y: 0, width: width, height: height)
    CGContextDrawImage(context, rect, img)

}

deinit {
}


func color_at(x: Int, y: Int)->(Int, Int, Int, Int) {

    assert(0<=x && x<width)
    assert(0<=y && y<height)

    let uncasted_data = CGBitmapContextGetData(context)
    let data = UnsafePointer<UInt8>(uncasted_data)

    let offset = 4 * (y * width + x)

    let alpha = data[offset]
    let red = data[offset+1]
    let green = data[offset+2]
    let blue = data[offset+3]

    let color = (Int(red), Int(green), Int(blue), Int(alpha))
    return color
    }
}

When I am declaring instances of Bitmap in my "main" function, all works fine. However, when I am using it in functions, the associated memory does not get released when the function terminates. As I am converting images, the RAM increases constantly and my iMac crashes. Any thoughts why the memory does not get related?

To illustrate:

func fingerprintImage(fileName: String) -> Int {

    //
    // Load the image and convert to bitmap
    //
    let url = NSURL(fileURLWithPath: fileName)
    let image:CIImage=CIImage(contentsOfURL: url)!
    let bitmap = Bitmap(img: convertCIImageToCGImage(image)) 
    return 0
}

let fileManager = NSFileManager.defaultManager()
let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath("<... PATH TO DIRECTORY WITH IMAGES, EG CANON CR2>")!
while let element = enumerator.nextObject() as? String {
    if element.hasSuffix("CR2") { 
        var fp2 = fingerprintImage(""<... PATH TO DIRECTORY WITH IMAGES, EG CANON CR2>"/"+element)
    }
}

I would have expected that bitmap gets released when fingerprintImage terminates but that is not the case - after about 50 images I run out of memory.

Community
  • 1
  • 1
mercator
  • 99
  • 1
  • 10
  • "when I am using it in functions" But you don't say what on earth that means, so no one can help. Use Instruments to see what is leaking. It might or might not have anything to do with the code you have shown. – matt Mar 29 '16 at 19:18
  • Thanks. Added code fragment in in the post: – mercator Mar 29 '16 at 19:23
  • plz also show convertCIImageToCGImage – Daij-Djan Mar 29 '16 at 20:58

1 Answers1

4

the associated memory does not get released when the function terminates

That is completely normal for autoreleased memory. You shouldn't expect memory to be reclaimed when functions terminate. You should expect it to be reclaimed when the autorelease pool drains. This is done automatically at the end of each event loop, which occurs between drawing cycles on iOS for example, but if you keep calling fingerprintImage in a loop, or otherwise running it in such a way that the autorelease pool doesn't drain for you automatically, you'll need to do it yourself.

In your example (a loop), you would generally do something like:

while let element = enumerator.nextObject() as? String {
    autoreleasepool {
        if element.hasSuffix("CR2") { 
            var fp2 = fingerprintImage(""<... PATH TO DIRECTORY WITH IMAGES, EG CANON CR2>"/"+element)
            // ...
        }
    }
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Hi Rob, Thanks a lot, that seems to work. Best, Helmut – mercator Mar 29 '16 at 19:52
  • Great. Which method is autoreleased? I can't find anything in the documentation for CGBitmapContextCreate which points to an autorelease pool. – Darko Mar 29 '16 at 20:36
  • 1
    @Darko see the ownership rule: https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html and ASSUME that _objects_ you don't own, are autoreleased. – Daij-Djan Mar 29 '16 at 20:55
  • @Daij-Djan I can't find anything regarding autorelease there. And no, manualy freeing the context is not needed in Swift. – Darko Mar 29 '16 at 21:00
  • @Darko Any function or method you call can generate autoreleased objects. This isn't specific to `CGBitmapContextCreate` and won't be documented on a function-by-function basis. It isn't even promised to stay the same between releases. You just have to assume it's going to happen and plan accordingly if you have loops that include function calls. – Rob Napier Mar 29 '16 at 22:16
  • @RobNapier Thanks Rob, didn't know that. – Darko Mar 30 '16 at 04:12