While trying to control the following two scenes in 1 ImmersiveSpace, we found the following memory leak when we background the app while a stereoscopic video is playing.
ImmersiveView
's two scenes:
- Scene 1 has 1 toggle button
- Scene 2 has same toggle button with a 180 degree skysphere playing a stereoscopic video
Attached are the files and images of the memory leak as captured in Xcode.
To replicate this memory leak, follow these steps:
- Create a new visionOS app using Xcode template as illustrated below.
- Configure the project to launch directly into an immersive space (set
Preferred Default Scene Session Role
toImmersive Space Application Session Role
in Info.plist. - Replace all swift files with those you will find in the attached texts.
- In
ImmersiveView
, replace the stereoscopic video to play with a large 3d 180 degree video of your own bundled in your project. - Launch the app in debug mode via Xcode and onto the AVP device or simulator
- Display the memory use by pressing on keys
command+7
and selectingMemory
in order to view the live memory graph - Press on the first immersive space's button "Open ImmersiveView"
- Press on the second immersive space's button "Show Immersive Video"
- Background the app
- When the app tray appears, foreground the app by selecting it
- The first immersive space should appear
- Repeat steps 7, 8, 9, and 10 multiple times
- Observe the memory use going up, the graph should look similar to the below illustration.
In ImmersiveView
, upon backgrounding the app, I do:
- a reset method to clear the video's memory
- dismiss of the Immersive Space containing the video (even though upon execution, visionOS raises the purple warning "Unable to dismiss an Immersive Space since none is opened". It appears visionOS dismisses any ImmersiveSpace upon backgrounding, which makes sense..)
Am I not releasing the memory correctly?
Or, is there really a memory leak issue in either SwiftUI's ImmersiveSpace
or in AVFoundation's AVPlayer
upon background of an app?
Hey @VaiStardom,
Upon further investigation this behaves correctly. There is a memory leak in your view model where your completion block in addObserver(forName:object:queue:using:)
is capturing the view model preventing it from being destroyed. To avoid a retain cycle, use a weak reference to self inside the block when self contains the observer as a strong reference.
In your case this would look like the following:
NotificationCenter.default.addObserver(
forName: .AVPlayerItemDidPlayToEndTime,
object: avPlayer.currentItem,
queue: .main) { [weak self] _ in
guard let self else { return }
Task {
self.avPlayer.seek(to: .zero)
self.avPlayer.play()
}
}
Let me know if you have additional questions,
Michael