mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
feat(spine-ios): Add memory leak detection and fix PMA handling
- Enable debug extension on app startup for leak detection - Add reportLeaks() calls when example views disappear - Fix PMA flag handling by reading it from atlas page instead of hardcoding to false - Add manual dispose() method to SpineController for explicit cleanup if needed Note: SwiftUI view caching may show false positive leaks when views disappear, as SwiftUI keeps views in memory for performance until they're truly no longer needed.
This commit is contained in:
parent
cb62bd70c0
commit
ab53d271a4
@ -28,35 +28,60 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import SpineiOS
|
import SpineiOS
|
||||||
|
import SpineSwift
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
|
// View modifier to report memory leaks when view disappears
|
||||||
|
struct LeakReporter: ViewModifier {
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.onDisappear {
|
||||||
|
reportLeaks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
func reportLeaksOnDisappear() -> some View {
|
||||||
|
modifier(LeakReporter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct MainView: View {
|
struct MainView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
Section {
|
Section {
|
||||||
NavigationLink("Simple Animation") {
|
NavigationLink("Simple Animation") {
|
||||||
SimpleAnimation()
|
SimpleAnimation()
|
||||||
|
.reportLeaksOnDisappear()
|
||||||
}
|
}
|
||||||
NavigationLink("Play/Pause") {
|
NavigationLink("Play/Pause") {
|
||||||
PlayPauseAnimation()
|
PlayPauseAnimation()
|
||||||
|
.reportLeaksOnDisappear()
|
||||||
}
|
}
|
||||||
NavigationLink("Animation State Listener") {
|
NavigationLink("Animation State Listener") {
|
||||||
AnimationStateEvents()
|
AnimationStateEvents()
|
||||||
|
.reportLeaksOnDisappear()
|
||||||
}
|
}
|
||||||
NavigationLink("Debug Rendering") {
|
NavigationLink("Debug Rendering") {
|
||||||
DebugRendering()
|
DebugRendering()
|
||||||
|
.reportLeaksOnDisappear()
|
||||||
}
|
}
|
||||||
NavigationLink("Dress Up") {
|
NavigationLink("Dress Up") {
|
||||||
DressUp()
|
DressUp()
|
||||||
|
.reportLeaksOnDisappear()
|
||||||
}
|
}
|
||||||
NavigationLink("IK Following") {
|
NavigationLink("IK Following") {
|
||||||
IKFollowing()
|
IKFollowing()
|
||||||
|
.reportLeaksOnDisappear()
|
||||||
}
|
}
|
||||||
NavigationLink("Physics") {
|
NavigationLink("Physics") {
|
||||||
Physics()
|
Physics()
|
||||||
|
.reportLeaksOnDisappear()
|
||||||
}
|
}
|
||||||
NavigationLink("Disable Rendering") {
|
NavigationLink("Disable Rendering") {
|
||||||
DisableRendering()
|
DisableRendering()
|
||||||
|
.reportLeaksOnDisappear()
|
||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("Swift + SwiftUI")
|
Text("Swift + SwiftUI")
|
||||||
@ -66,6 +91,7 @@ struct MainView: View {
|
|||||||
SimpleAnimationViewControllerRepresentable()
|
SimpleAnimationViewControllerRepresentable()
|
||||||
.navigationTitle("Simple Animation")
|
.navigationTitle("Simple Animation")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.reportLeaksOnDisappear()
|
||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("ObjC + UIKit")
|
Text("ObjC + UIKit")
|
||||||
|
|||||||
@ -28,11 +28,17 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import SpineiOS
|
import SpineiOS
|
||||||
|
import SpineSwift
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct SpineExampleApp: App {
|
struct SpineExampleApp: App {
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// Enable debug extension for memory leak detection
|
||||||
|
enableDebugExtension(true)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
|
|||||||
@ -110,6 +110,13 @@ public final class SpineController: NSObject, ObservableObject {
|
|||||||
drawable?.dispose() // TODO move drawable out of view?
|
drawable?.dispose() // TODO move drawable out of view?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Manually dispose the drawable. Call this when you know the controller is no longer needed.
|
||||||
|
/// This is useful in SwiftUI where views may be cached and deinit may be delayed.
|
||||||
|
public func dispose() {
|
||||||
|
drawable?.dispose()
|
||||||
|
drawable = nil
|
||||||
|
}
|
||||||
|
|
||||||
/// The ``Atlas`` from which images to render the skeleton are sourced.
|
/// The ``Atlas`` from which images to render the skeleton are sourced.
|
||||||
public var atlas: Atlas {
|
public var atlas: Atlas {
|
||||||
|
|||||||
@ -247,12 +247,15 @@ extension SpineUIView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func initRenderer(atlasPages: [UIImage]) throws {
|
private func initRenderer(atlasPages: [UIImage]) throws {
|
||||||
|
// Get PMA flag from first atlas page if available
|
||||||
|
let pmaFlag = controller.atlas.pages.count > 0 ? (controller.atlas.pages[0]?.pma ?? false) : false
|
||||||
|
|
||||||
renderer = try SpineRenderer(
|
renderer = try SpineRenderer(
|
||||||
device: SpineObjects.shared.device,
|
device: SpineObjects.shared.device,
|
||||||
commandQueue: SpineObjects.shared.commandQueue,
|
commandQueue: SpineObjects.shared.commandQueue,
|
||||||
pixelFormat: colorPixelFormat,
|
pixelFormat: colorPixelFormat,
|
||||||
atlasPages: atlasPages,
|
atlasPages: atlasPages,
|
||||||
pma: false // TODO: Get PMA flag from atlas when API is available
|
pma: pmaFlag
|
||||||
)
|
)
|
||||||
renderer?.delegate = controller
|
renderer?.delegate = controller
|
||||||
renderer?.dataSource = controller
|
renderer?.dataSource = controller
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user