skip to Main Content

I have a problem: In a bigger project with a similar structure I am getting a memory Leak from the Instrument Tool.

If you are putting this in Xcode and run, you should see a line which is moving to the right side and then to the left.
When the Button is pressed, the Line jumps to a defined position.
If you use the Memory Leak Tool there is an Error after the Button is tapped. I don’t know why. Is this a bug? Do I have a basic mistake in the code? How to avoid this?

Is this the right way of connecting the animation calculation with SwiftUI, that I have the SKScene as an ObservableObject and mark the animation as @Published for doing this animation?

I appreciate any answers.

In the leakage Tool is a note, that the responsible frame is:

[NSXPCConnection remoteObjectProxyWithErrorHandler:]

Thanks for reading, here is the example code:
Overview

Animation Calc

    import SwiftUI
    import SpriteKit

    struct MyAnimation{

        var lenght:CGFloat = 0  //Position of the line end
        var up: Bool = true  //if the line is moving to right or left

        mutating func change(){
        
            self.up ? (lenght += 1) : (lenght -= 1)
        
            if lenght > 100{
                up = false
            }else if lenght < 0{
                up = true
            }
        }
    }

SKScene for updating


    class GameScene: SKScene, ObservableObject{
    
        @Published var ani: MyAnimation  //handles the calculation
    
        override init(){
            ani = MyAnimation()
            super.init(size: CGSize(width: 200, height: 100))
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override func update(_ currentTime: TimeInterval) {
            ani.change()  //new Position is calculated
        }
    }

Content View


    struct ContentView: View {
    
        @StateObject var game = GameScene()
    
        var body: some View {
            VStack{
                ZStack{
                    SpriteView(scene: game).opacity(0)
                    MyPath().environmentObject(game)
                }
                Start().environmentObject(game)  
                //Button to let the line jump to the defined position
        
            }
        }
    }

Path to animate


    struct MyPath: View{
        
        @EnvironmentObject var game: GameScene
        
        var body: some View{
            Path{ path in
                path.move(to: CGPoint(x: 50, y: 50))
                path.addLine(to: CGPoint(x: 200 + game.ani.lenght, y: 220))  
                //here is the length property of the MyAnimation struct and should cause the redraw

                path.closeSubpath()
            }
            .stroke(Color.black, lineWidth: 4)
        }
    }

Button


    struct Start: View {    //Button
        
        @EnvironmentObject var game: GameScene
        
        var body: some View {
            Button(action: {
                game.isPaused = true
                game.ani.lenght = 30
                game.isPaused = false
            }, label: {
                Text("Start")
            })
        }
    }

For Copy Paste

    import SwiftUI
    import SpriteKit

    struct MyAnimation{

        var lenght:CGFloat = 0  //Position of the line end
        var up: Bool = true  //if the line is moving to right or left

        mutating func change(){
        
            self.up ? (lenght += 1) : (lenght -= 1)
        
            if lenght > 100{
                up = false
            }else if lenght < 0{
                up = true
            }
        }
    }


    class GameScene: SKScene, ObservableObject{
    
        @Published var ani: MyAnimation  //handles the calculation
    
        override init(){
            ani = MyAnimation()
            super.init(size: CGSize(width: 200, height: 100))
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override func update(_ currentTime: TimeInterval) {
            ani.change()  //new Position is calculated
        }
    }



    struct ContentView: View {
    
        @StateObject var game = GameScene()
    
        var body: some View {
            VStack{
                ZStack{
                    SpriteView(scene: game).opacity(0)
                    MyPath().environmentObject(game)
                }
                Start().environmentObject(game)  
                //Button to let the line jump to the defined position
        
            }
        }
    }


    struct MyPath: View{
        
        @EnvironmentObject var game: GameScene
        
        var body: some View{
            Path{ path in
                path.move(to: CGPoint(x: 50, y: 50))
                path.addLine(to: CGPoint(x: 200 + game.ani.lenght, y: 220))  
                //here is the length property of the MyAnimation struct and should cause the redraw

                path.closeSubpath()
            }
            .stroke(Color.black, lineWidth: 4)
        }
    }


    struct Start: View {    //Button
        
        @EnvironmentObject var game: GameScene
        
        var body: some View {
            Button(action: {
                game.isPaused = true
                game.ani.lenght = 30
                game.isPaused = false
            }, label: {
                Text("Start")
            })
        }
    }

2

Answers


  1. I could reproduce the same leak with the following code:

      import SwiftUI
    
      struct ContentView: View {
        var body: some View {
            VStack{
                Start()
            }
        }
      }
    
      struct Start: View {
        var body: some View {
            Button(action: {
            }, label: {
                Text("Start")
            })
        }
      }
    

    Without having more insights into this issue, I would assume that you are not responsible for the leak, but Apple is.

    I think you have no choice but to ignore the leak for now if you still wish to use SwiftUI.
    By the way, I had worse issues with SwiftUI, and I have abandoned it for now because I believe it just isn’t ready yet.

    Login or Signup to reply.
  2. I solved it by using NotificationCenter to update my skscene

    NotificationCenter.default.addObserver(forName: "updateLevel", object: nil, queue: nil,
                                      using: { [weak self]_ in
                      self?.updateLevel()
                  })
    

    in swiftUI View:

    Button(action: {
                       NotificationCenter.default.post(name: "updateLevel", object: nil, userInfo: ["useAI":false])
                   }, label: {
                       Image("restart")
                           .resizable()
                           .frame(width: 110, height: 110)
                           
                   })
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search