iOS iPhone how to solve didReceiveMemoryWarning?

I recently needed to implement the music visualizer in iOS swift as shown in the image/gif.

visualizer gif

I used this TempiFFT to precesses the audio input and modified the UI for the required visual effects. the main features of the visual effects are: 1) the gradient of the bars 2) the dashed effect in the bars 3) fading out the previous bars

for the fading effect i used this How To Make A Simple Drawing App with UIKit and Swift where it initially draws on temporary image and then copies to the main image with the reduced opacity (if required).

I somehow successfully implemented the features. The bars work properly but “didReceiveMemoryWarning” is called after sometime (around 3 minutes) and the app stops few seconds after that.

So how do I resolve the memory warning issue and the app stops issue

Following is the code for drawing bars:

    func drawBars() {
// start drawng on temp image
UIGraphicsBeginImageContext(self.tempImage.frame.size)
// get context
let context = UIGraphicsGetCurrentContext()
tempImage.image?.draw(in: CGRect(x:0, y:0, width: tempImage.frame.width, height:tempImage.frame.height))


let height = Double(self.tempImage.frame.size.height)
let width = Double(self.tempImage.frame.size.width)

let barWidth = width / bars
//let barSpace = width / bars * 0.2

// number of bars
let count = self.fft?.numberOfBands ?? 0

// number of magnitudes
let fft = self.fft?.bandMagnitudes ?? [0, 0, 0]


if count == 0 {
    return
}

// Draw the spectrum.
let maxDB: Float = 64.0
let minDB: Float = -32.0
let headroom = maxDB - minDB
//let colWidth = tempi_round_device_scale(d: CGFloat(width) / CGFloat(count))

// need to display only few (10) bars so will consider very (count/10)th bar
let divider = count / Int(bars) - 1
var barCount = 0

for i in 0..<count {
    // checking if display bar or not
    if i % divider != 0 || i / divider > 9 {
        continue
    }

    let magnitude = fft[i]

    // Incoming magnitudes are linear, making it impossible to see very low or very high values. Decibels to the rescue!
    var magnitudeDB = TempiFFT.toDB(magnitude)

    // Normalize the incoming magnitude so that -Inf = 0
    magnitudeDB = max(0, magnitudeDB + abs(minDB))

    let dbRatio = min(1.0, magnitudeDB / headroom)
    let magnitudeNorm = CGFloat(dbRatio) * CGFloat(height)

    //let colRect: CGRect = CGRect(x: x, y: plotYStart, width: colWidth, height: magnitudeNorm)

    //let startPoint = CGPoint(x: viewWidth / 2, y: 0)
    //let endPoint = CGPoint(x: viewWidth / 2, y: viewHeight)

    //context.saveGState()
    //context.clip(to: colRect)
    //context.drawLinearGradient(gradient!, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(rawValue: 0))
    //context.restoreGState()

    //x += colWidth
    //}

    //for var i in (0..<10) {

    //print(barCount)

    let x = Double(barCount) * barWidth + 0.5 * barWidth

    // cliping the bar area for dash and gradient effect
    context?.saveGState()
    context?.addRect(CGRect(x:x - width / bars * 0.4, y:height - Double(magnitudeNorm), width:width / bars * 0.8, height:Double(magnitudeNorm)))
    context?.clip()
    //            context?.drawLinearGradient(CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: [UIColor.red.cgColor, UIColor.blue.cgColor] as CFArray, locations: [0, 1])! , start: CGPoint(x:x, y:height + 5), end: CGPoint(x:x, y:height - Double(magnitudeNorm)), options: CGGradientDrawingOptions(rawValue: 0))
    // draw the gradient first
    context?.drawLinearGradient(CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: [UIColor.white.cgColor, UIColor.cyan.cgColor, UIColor.magenta.cgColor, UIColor.magenta.cgColor] as CFArray, locations: [0, 0.5, 0.85,   1])! , start: CGPoint(x:x, y:height + 5), end: CGPoint(x:x, y:0), options: CGGradientDrawingOptions(rawValue: 0))

    context?.restoreGState()

    //context?.addRect(CGRect(x:x - width / bars * 0.4, y:height - Double(magnitudeNorm), width:width / bars * 0.8, height:Double(magnitudeNorm)))
    //context?.drawPath(using: CGPathDrawingMode.stroke)

    // now draw the dash line
    context?.move(to: CGPoint(x:x, y:height + 5))
    context?.addLine(to: CGPoint(x:x, y:height - Double(magnitudeNorm)))
    //context?.addLine(to: CGPoint(x:x, y:height - Double((i / divider + 1) * 10)))

    context?.setLineCap(CGLineCap.butt)
    context?.setLineWidth(CGFloat(width / bars * 0.85))
    context?.setStrokeColor(red: 0, green: 0, blue: 0, alpha: 1)
    context?.setBlendMode(CGBlendMode.normal)
    context?.setLineDash(phase: 1, lengths: [5, 10])

    context?.strokePath()

    barCount += 1;

}

// save the image
tempImage.image = UIGraphicsGetImageFromCurrentImageContext()

tempImage.alpha = 1
UIGraphicsEndImageContext()

tempi_dispatch_main { () -> () in
    //self.spectralView.fft = fft
    //self.spectralView.setNeedsDisplay()
    //merge the image with the previous image
    //on main thread
    self.refresh()
}

//refresh()

}

func refresh() {
UIGraphicsBeginImageContext(mainImage.frame.size)
//let context = UIGraphicsGetCurrentContext()
// draw the previous image with 0.9 opacity(transparancy) for a fade out effect
mainImage.image?.draw(in: CGRect(x:0, y:0, width:mainImage.frame.width, height:mainImage.frame.height), blendMode: CGBlendMode.normal, alpha: 0.9)
// draw the new bars over the previous one
tempImage.image?.draw(in: CGRect(x:0, y:0, width:mainImage.frame.width, height:mainImage.frame.height), blendMode: CGBlendMode.normal, alpha: 1)
// finally show that new image on the image view
mainImage.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

tempImage.image = nil

}

hi @tonnyfox,
I haven’t run the code but what you are describing, have you tried to look at instruments to see if you are having memory leaks. You can also look at this wonderful book https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering

cheers,