ARTICLE AD BOX
I'm following along this camera tutorial from Apple: https://developer.apple.com/tutorials/sample-apps/capturingphotos-camerapreview
I have a DataModel:
final class DataModel: ObservableObject { let camera = Camera() @Published var frame: Image? var isPhotosLoaded = false init() { print("DataModel init") Task { await handleCameraPreviews() } } deinit { print("DataModel deinit") } func handleCameraPreviews() async { let imageStream = camera.previewStream .map { $0.image } for await image in imageStream { Task { @MainActor in // CIFilters to come... frame = image } } } }And the Camera:
class Camera: NSObject { ... deinit() { print("Camera > deinit") } private var addToPreviewStream: ((CIImage) -> Void)? lazy var previewStream: AsyncStream<CIImage> = { AsyncStream { continuation in addToPreviewStream = { ciImage in if !self.isPreviewPaused { continuation.yield(ciImage) } } } }() ... } extension Camera: AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let pixelBuffer = sampleBuffer.imageBuffer else { return } addToPreviewStream?(CIImage(cvPixelBuffer: pixelBuffer)) } }This works well, until I noticed the memory usage climbing every time I switch to a 2nd view and back to the camera again.
Every time I navigate to the 2nd view, I never see "deinit" printed from either the DataModel() or the Camera(). And when I navigate back to the camera view, I see the both the DataModel() and the Camera() print "init".
I admit I understand very little about memory management, but I suspect there is a strong retain cycle.
When I comment out the handleCameraPreviews() function, the problem goes away. I suspect that's where the retain cycle or some other leak is coming from because both objects init and deinit as expected.
I tried updating the handleCameraPreviews() to add [weak self]:
func handleCameraPreviews() async { let stream = camera.previewStream for await frame in stream { try Task.checkCancellation() await MainActor.run { [weak self] in guard let self else { return } self.frame = frame.image } } }But it didn't help. Unfortunately, this is where I'm out of ideas due to limits of my knowledge. Any suggestions?
Thank you!
