Do we need to remove and nil everything with Storyboard ?

Hey there !

I have a leak memory (explained here) and, to found where it was coming from, I had to make a simple test. You can see or download the code in the previous post.

I’m just creating 2 scenes and in both of theme, there’s a simple button with a touch event. Once touched, it goes to the scene 1 or 2. 

I found out that, before calling gotoScene(), if I don’t remove the button and nil it, memory keeps growing while going scene to scene. 

But, i thought that the storyboard API was removing automatically everything inserted in the scene.view…

So, do we really have to remove and nil every display object created in a scene ? 

I’m fairly certain that anything added to the scene.view, and any other local variables created with a scene, are removed by Storyboard/Composer without having to nil references manually. When you create the objects are you making sure they are local variables?

Yes, they are. Here is my simple test. You’ll see that the LUA Memory keeps increasing. And it won’t when the removeSelf() and nil lines are uncommented…

main.lua

local storyboard = require "storyboard" local scene = storyboard.newScene() storyboard.purgeOnSceneChange = true ------------------------------------------------------------------- -- HIDE STATUS BAR ------------------------------------------------------------------- display.setStatusBar(display.HiddenStatusBar) ------------------------------------------------------------------- -- MONITORING ------------------------------------------------------------------- local monitorMem = function() collectgarbage() print("MemUsage - Maingame : " .. collectgarbage("count")) local textMem = system.getInfo("textureMemoryUsed") / 1000000 print( "TexMem: " .. textMem ) end Runtime:addEventListener("enterFrame", monitorMem) ------------------------------------------------------------------- -- SHOWING THE STARTSCREEN ------------------------------------------------------------------- storyboard.gotoScene("screen1")

screen1.lua

local storyboard = require("storyboard") local scene = storyboard.newScene() ------------------------------------------------------------------- -- STORYBOARD - CREATESCENE ------------------------------------------------------------------- function scene:createScene(event) local gameGroup = self.view local button -- Function that will be called later local function gotoScene(event) if event.phase == "ended" then --button:removeSelf(); --button = nil; storyboard.gotoScene("screen2") end end -- We create a button button = display.newRect(0,0,200,200) button:setFillColor(1,0,0) button.x = display.contentHeight\*0.5; button.y = display.contentHeight\*0.5; print(button.x) -- On which we apply a touch event listener button:addEventListener("touch", gotoScene) -- Button is inserted into gameGroup gameGroup:insert(button) end ------------------------------------------------------------------- -- STORYBOARD - ENTERSCENE ------------------------------------------------------------------- function scene:enterScene(event) local gameGroup = self.view storyboard.removeScene("screen2") end ------------------------------------------------------------------- -- STORYBOARD - ENTERSCENE ------------------------------------------------------------------- function scene:exitScene(event) local gameGroup = self.view storyboard.removeScene("screen1") end scene:addEventListener("createScene", scene) scene:addEventListener("enterScene", scene) scene:addEventListener("exitScene", scene) return scene

screen2.lua

------------------------------------------------------------------- -- LOADING EXTERNAL MODULES ------------------------------------------------------------------- local storyboard = require("storyboard") local scene = storyboard.newScene() ------------------------------------------------------------------- -- STORYBOARD - CREATESCENE ------------------------------------------------------------------- function scene:createScene(event) local gameGroup = self.view local button -- Function that will be called later local function gotoScene(event) if event.phase == "ended" then --button:removeSelf(); --button = nil; storyboard.gotoScene("screen1") end end -- We create a button button = display.newRect(0,0,200,200) button:setFillColor(0,1,0) button.x = display.contentHeight\*0.5; button.y = display.contentHeight\*0.5; -- On which we apply a touch event listener button:addEventListener("touch", gotoScene) -- Button is inserted into gameGroup gameGroup:insert(button) end ------------------------------------------------------------------- -- STORYBOARD - ENTERSCENE ------------------------------------------------------------------- function scene:enterScene(event) local gameGroup = self.view storyboard.removeScene("main") end ------------------------------------------------------------------- -- STORYBOARD - ENTERSCENE ------------------------------------------------------------------- function scene:exitScene(event) local gameGroup = self.view storyboard.removeScene("screen2") end scene:addEventListener("createScene", scene) scene:addEventListener("enterScene", scene) scene:addEventListener("exitScene", scene) return scene

How long are you waiting before switching scenes again? It does take a few seconds for the memory to release.

Long enough I think : waiting 10 seconds before switching doesn’t change anything.

you’re never actually removing screen1.  several problems, first being that “main” need not/should not create a scene itself - it’ll never be displayed.

then your “removal” flow is a bit mixed up:

  screen1 removes screen2 upon enter

  screen2 removes main upon enter

the first is ok, tho not how I’d do it.  the second is sort of silly, as main shouldn’t have created a scene.  and no other scene ever tries to remove screen1 on enter.

the real problem, though, is what you assume is happening in your exit events:

  screen1 (tries to) remove itself

  screen2 (tries to) remove itself

neither of those is going to work, will do nothing, storyboard/composer forbid you from removing the current scene - and for good reason.  (think about it:  the code you’re running *right now* that called removeScene() exists in the scene you’re trying to remove - you can’t remove code that’s currently running, where would removeScene() return to?)

simple fix?  get rid of *ALL* the removeScene()'s wherever they occur.

put a single storyboard.removeAll() in every scene’s enter event.

(removeAll is really “remove all EXCEPT current scene”, for same reason as above, but will clean up all other scenes for you)

hth

davebollinger : PERFECT ! This works perfectly now ! Thank you for the explanation too !

Stlil got one last question though. I’m not sure to understand this : “several problems, first being that “main” need not/should not create a scene itself - it’ll never be displayed.” 

ask: do you ever do “storyboard.gotoScene(“main”)”?  no, nor would you want to, main is like a one-time “bootstrap”

[aside:  fix still valid, but my analysis of your exit flow was written too fast without thinking it through.  by that point (exit event), storyboard would no longer consider “this” scene “current”, so would actually *try* to remove it, but it would fail to actually remove the module due to the active code - Lua’s too smart to allow that, but it’ll be enough that storyboard will think it needs to recreate it next time, thus memory leak.  same/similar effect, just slightly different cause]

Ok I understand now.

And about the “removeScene(“main”)”, it was actually supposed to be “removeScene(“scene1”)” instead. Even if it wouldn’t have changed much.

But now, I fully understand and it works, so thanks a lot ! :slight_smile:

I’m fairly certain that anything added to the scene.view, and any other local variables created with a scene, are removed by Storyboard/Composer without having to nil references manually. When you create the objects are you making sure they are local variables?

Yes, they are. Here is my simple test. You’ll see that the LUA Memory keeps increasing. And it won’t when the removeSelf() and nil lines are uncommented…

main.lua

local storyboard = require "storyboard" local scene = storyboard.newScene() storyboard.purgeOnSceneChange = true ------------------------------------------------------------------- -- HIDE STATUS BAR ------------------------------------------------------------------- display.setStatusBar(display.HiddenStatusBar) ------------------------------------------------------------------- -- MONITORING ------------------------------------------------------------------- local monitorMem = function() collectgarbage() print("MemUsage - Maingame : " .. collectgarbage("count")) local textMem = system.getInfo("textureMemoryUsed") / 1000000 print( "TexMem: " .. textMem ) end Runtime:addEventListener("enterFrame", monitorMem) ------------------------------------------------------------------- -- SHOWING THE STARTSCREEN ------------------------------------------------------------------- storyboard.gotoScene("screen1")

screen1.lua

local storyboard = require("storyboard") local scene = storyboard.newScene() ------------------------------------------------------------------- -- STORYBOARD - CREATESCENE ------------------------------------------------------------------- function scene:createScene(event) local gameGroup = self.view local button -- Function that will be called later local function gotoScene(event) if event.phase == "ended" then --button:removeSelf(); --button = nil; storyboard.gotoScene("screen2") end end -- We create a button button = display.newRect(0,0,200,200) button:setFillColor(1,0,0) button.x = display.contentHeight\*0.5; button.y = display.contentHeight\*0.5; print(button.x) -- On which we apply a touch event listener button:addEventListener("touch", gotoScene) -- Button is inserted into gameGroup gameGroup:insert(button) end ------------------------------------------------------------------- -- STORYBOARD - ENTERSCENE ------------------------------------------------------------------- function scene:enterScene(event) local gameGroup = self.view storyboard.removeScene("screen2") end ------------------------------------------------------------------- -- STORYBOARD - ENTERSCENE ------------------------------------------------------------------- function scene:exitScene(event) local gameGroup = self.view storyboard.removeScene("screen1") end scene:addEventListener("createScene", scene) scene:addEventListener("enterScene", scene) scene:addEventListener("exitScene", scene) return scene

screen2.lua

------------------------------------------------------------------- -- LOADING EXTERNAL MODULES ------------------------------------------------------------------- local storyboard = require("storyboard") local scene = storyboard.newScene() ------------------------------------------------------------------- -- STORYBOARD - CREATESCENE ------------------------------------------------------------------- function scene:createScene(event) local gameGroup = self.view local button -- Function that will be called later local function gotoScene(event) if event.phase == "ended" then --button:removeSelf(); --button = nil; storyboard.gotoScene("screen1") end end -- We create a button button = display.newRect(0,0,200,200) button:setFillColor(0,1,0) button.x = display.contentHeight\*0.5; button.y = display.contentHeight\*0.5; -- On which we apply a touch event listener button:addEventListener("touch", gotoScene) -- Button is inserted into gameGroup gameGroup:insert(button) end ------------------------------------------------------------------- -- STORYBOARD - ENTERSCENE ------------------------------------------------------------------- function scene:enterScene(event) local gameGroup = self.view storyboard.removeScene("main") end ------------------------------------------------------------------- -- STORYBOARD - ENTERSCENE ------------------------------------------------------------------- function scene:exitScene(event) local gameGroup = self.view storyboard.removeScene("screen2") end scene:addEventListener("createScene", scene) scene:addEventListener("enterScene", scene) scene:addEventListener("exitScene", scene) return scene

How long are you waiting before switching scenes again? It does take a few seconds for the memory to release.

Long enough I think : waiting 10 seconds before switching doesn’t change anything.

you’re never actually removing screen1.  several problems, first being that “main” need not/should not create a scene itself - it’ll never be displayed.

then your “removal” flow is a bit mixed up:

  screen1 removes screen2 upon enter

  screen2 removes main upon enter

the first is ok, tho not how I’d do it.  the second is sort of silly, as main shouldn’t have created a scene.  and no other scene ever tries to remove screen1 on enter.

the real problem, though, is what you assume is happening in your exit events:

  screen1 (tries to) remove itself

  screen2 (tries to) remove itself

neither of those is going to work, will do nothing, storyboard/composer forbid you from removing the current scene - and for good reason.  (think about it:  the code you’re running *right now* that called removeScene() exists in the scene you’re trying to remove - you can’t remove code that’s currently running, where would removeScene() return to?)

simple fix?  get rid of *ALL* the removeScene()'s wherever they occur.

put a single storyboard.removeAll() in every scene’s enter event.

(removeAll is really “remove all EXCEPT current scene”, for same reason as above, but will clean up all other scenes for you)

hth

davebollinger : PERFECT ! This works perfectly now ! Thank you for the explanation too !

Stlil got one last question though. I’m not sure to understand this : “several problems, first being that “main” need not/should not create a scene itself - it’ll never be displayed.” 

ask: do you ever do “storyboard.gotoScene(“main”)”?  no, nor would you want to, main is like a one-time “bootstrap”

[aside:  fix still valid, but my analysis of your exit flow was written too fast without thinking it through.  by that point (exit event), storyboard would no longer consider “this” scene “current”, so would actually *try* to remove it, but it would fail to actually remove the module due to the active code - Lua’s too smart to allow that, but it’ll be enough that storyboard will think it needs to recreate it next time, thus memory leak.  same/similar effect, just slightly different cause]

Ok I understand now.

And about the “removeScene(“main”)”, it was actually supposed to be “removeScene(“scene1”)” instead. Even if it wouldn’t have changed much.

But now, I fully understand and it works, so thanks a lot ! :slight_smile: