Continuous loop playback on AVAudioPlayer make memory leak?

Hello folks,

Now, I'm trying to make continuous loop playback function with AVAudioPlayer. After I implemented sample code as below, and I checked memory usage. As a result of 1 hour loop, memory usage kept increasing for a hour from start to end.

My app is expected to run for over 10 hours or over. so, I think that I need to solve memory leak problem.

Does someone know how to avoid kind of memory leak? My implementation way(code design standard or @escaping async function, etc...) is not good or not typical way for continuous audio loop playback? or The way to use AVAudioPlayer is not correct?

If someone knows that, that will save my life. Best regards,

Answered by hiro1900 in 684438022

I solved this problem. This audioplayback design was not good. Recursive call increase memory. So, I find out another design approach. Dependency Injection. And, then memory problem has been solved! 

Sorry. I forgot attaching my code.  

VIEW

import SwiftUI

struct ContentView: View {
   
  let apm = AudioPlayManager()
   
  var body: some View {
    Button(action: {
      self.apm.startPlayAudio(){ audioMangeCompletion in 
        print("play is finished")
      }
    }, label: {
      Text("Push")
    })
  }
}

AudioPlayManager

import SwiftUI
import AVFoundation


public class AudioPlayManager{
   
  let items:[String] = ["sound01", "sound02", "sound03"]
  let isLoopPlay = true
  var playingIndex = 0
   
  let apm = AudioPlayFunction.shared
   
  func startPlayAudio(completion: @escaping (Bool) -> Void){
     
    playAudio() { isAudioPlayComplete in
      completion(true)
    }
  }
   
  private func playAudio(completion: @escaping (Bool) -> Void){
    let i = playingIndex
     
    print("i: \(i) items.count: \(items.count)")
     
    if(i >= items.count && isLoopPlay){
      playingIndex = 0
      let fileURL = URL(fileURLWithPath: Bundle.main.path(forResource: items[0], ofType: "mp3") ?? "")
      print("fileURL: \(fileURL)")
      apm.playMP3(url: fileURL){ playMP3complete in
        self.playingIndex += 1
        self.playAudio() { playAudioCompletion in
          completion(true)
        }
      }
    } else {
      let fileURL = URL(fileURLWithPath: Bundle.main.path(forResource: items[i], ofType: "mp3") ?? "")
      print("fileURL: \(fileURL)")
      apm.playMP3(url: fileURL){ playMP3complete in
        self.playingIndex += 1
        self.playAudio() { playAudioCompletion in
          completion(true)
        }
      }
    }
  }
   
}

AudioPlayFunction

import Foundation
import AVFoundation

class AudioPlayFunction: NSObject, AVAudioPlayerDelegate {
  static let shared = AudioPlayFunction()
   
  var audioPlayer: AVAudioPlayer?
  private var observer: NSObjectProtocol?
   
  override init() {
    super.init()
  }
   
  func playMP3(url: URL, completion: @escaping (Bool) -> Void){
    do {
      print("url: \(url)")
      audioPlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
      audioPlayer?.delegate = self
      audioPlayer?.play()
      print("sound is playing")
    } catch let error {
      print("Sound Play Error -> \(error)")
    }
     
    if(observer == nil){
      print("create observer")
      observer = NotificationCenter.default.addObserver(forName: NSNotification.Name("audioStatus"), object: nil, queue: .main){ (_) in
         
        let speechStatus = UserDefaults.standard.value(forKey: "audioStatus") as? Bool ?? false
         
        if speechStatus {
          print("audio completion signal is received")
          completion(true)
        }
         
      }
    }
     
  }
       
  func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
    print("Audio Play Stop ")
    UserDefaults.standard.set(true, forKey: "audioStatus")
    NotificationCenter.default.post(name:NSNotification.Name("audioStatus"), object: nil)
  }
   
  public func stopSpeech(){
    self.audioPlayer?.stop()
     
    print("User stop audio play")
    UserDefaults.standard.set(true, forKey: "audioStatus")
    NotificationCenter.default.post(name:NSNotification.Name("audioStatus"), object: nil)
     
  }
}

Thanks

Accepted Answer

I solved this problem. This audioplayback design was not good. Recursive call increase memory. So, I find out another design approach. Dependency Injection. And, then memory problem has been solved! 

Continuous loop playback on AVAudioPlayer make memory leak?
 
 
Q