Transitions and Timers

Hi to all,

I am currently going through my code cleaning it up and hunting down memory leaks.

One cause of my memory leaks was creating text objects over and over themselves, like I was doing each time I updated my score.

local updateScore = function() score = score + 10 options = { ... } scGraphic = display.newText(options) mainGroup:insert( scGraphic ) end myTimer = timer.performWithDelay( 100, updateScore, 100)

This has lead me to a question regarding transitions and timers.

I use one variable for each and scope them at the top of my enterScene(),

local myTimer

local myTransition

Now, in my code I have multiple transitions and timers running at the same time, all using just those two variables.

For example this is moving 4 spaceships using 1 variable, myTransition:

for a = 1, 4 do

myTransition = transition.to( spaceship[a], { time=2000, alpha=0, x=spX[a], y=spY[a] } )

end

I’m curious, is this creating the same problem as the score scenario I mentioned above?

Yes. The loop is creating 4 transitions but you will only retain a variable reference to the last one. You could name them using tags (see the docs for this) or you could add them to a table, eg:

local myTransitions = {} for a=1, 4 do myTransitions[#myTransitions+1] = transition.to( spaceship[a], { time=2000, alpha=0, x=spX[a], y=spY[a] } ) end

I had a feeling that was the problem, I wish I knew this before I started making my game 9 months ago, it’s going to take me days to fix this, my code is massive and complex and I’ve got timers and transitions going on all over the place.

Thanks Horace

Sounds like the perfect time for some refactoring and unit testing.

Yes Horace,

I’ve been doing this for days, my code was very sloppy.

I’ve been making external modules to lean up the main chunks but I’m concerned that they are just adding to my memory leaks.

Following this… http://forums.coronalabs.com/topic/22091-guide-findingsolving-memory-leaks/

Has created this in just one of my 30 modules…

 buttons.passButton:removeEventListener("touch", buttons.passButton) buttons.button1:removeEventListener( "touch", buttons.button1 ) buttons.button2:removeEventListener( "touch", buttons.button2 ) buttons.button3:removeEventListener( "touch", buttons.button3 ) buttons.button4:removeEventListener( "touch", buttons.button4 ) if selftimer ~= nil then timer.cancel( selftimer ) selftimer = nil end if myTransition ~= nil then transition.cancel( myTransition ) myTransition = nil end display.remove(background) display.remove(cashSlider) display.remove(cashbackground) display.remove(letter1) display.remove(letter2) display.remove(letter3) display.remove(letter4) display.remove(textField1) if trueOrfalse == true then display.remove(textField2) display.remove(textField3) end display.remove(scGraphic) display.remove(questionCountGraphic) for i = 1, 20 do display.remove(coinSprite[i]) coinSprite[i] = nil end for i = 1, 10 do physics.removeBody( star[i], "dynamic") display.remove(star[i]) star[i] = nil end coinSprite = nil star = nil i = nil fontSheet = nil coinSheet = nil coinSprite = nil background = nil cashSlider = nil cashbackground = nil letter1 = nil letter2 = nil letter3 = nil letter4 = nil textField1 = nil textField2 = nil textField3 = nil textField4 = nil textField5 = nil questionCountGraphic = nil scGraphic = nil buttons.button1 = nil buttons.button2 = nil buttons.button3 = nil buttons.button4 = nil buttons.button5 = nil buttons.multiply5 = nil buttons.multiply4 = nil buttons.multiply3 = nil buttons.multiply2 = nil buttons.multiply1 = nil buttons.scoreCard = nil buttons.livesCard = nil buttons.passCard = nil buttons.passButton = nil buttons = nil audio.stop() audio.dispose(sfx.ticktock) audio.dispose(sfx.backSound) audio.dispose(sfx.fastSound) audio.dispose(sfx.passSound) audio.dispose(sfx.chinkSound) audio.dispose(sfx.wrongSound) audio.dispose(sfx.failSound) audio.dispose(sfx.coinSound) audio.dispose(sfx.furiousSound) audio.dispose(sfx.correctSound) audio.dispose(sfx.whooshSound) backSound = nil ticktockSound = nil wrongSound = nil chinkSound = nil passSound = nil correctSound = nil whooshSound = nil failSound = nil furiousSound = nil coinSound = nil sfx.ticktock = nil sfx.backSound = nil sfx.fastSound = nil sfx.passSound = nil sfx.chinkSound = nil sfx.wrongSound = nil sfx.failSound = nil sfx.coinSound = nil sfx.furiousSound = nil sfx.correctSound = nil sfx.whooshSound = nil sfx = nil collectgarbage( "collect" )

I’m quite sure I don’t need to do most of this because I declare everything local within the module, but I do it to make sure.

It seems quite mad that the guide says you have to remove all the display objects yourself?

Surely if the display object is inserted into the scene’s mainGroup it’s taken care of by the garbage disposal?

Also, disposing and nilling out all those sounds is just bloating my code.

And now I’ve got to rename all my Transitions and Timers, aarrgghh!

Back to work…

I find it better to make variable references upon the scene’s object rather than explicitly creating variables in the module. This is because they will be removed when the scene is cleaned up. You just need to make sure that what you think is being cleaned up actually is.

If you were to put all of those references into tables and then nil out the table (after removing the listeners) you would be making the cleanup easier.

Hi Horace,

Like what I’ve done with the buttons?

So if I just nil out the buttons table then all the objects in that table would be nilled out too?

buttons = nil would nil out buttons.button1, buttons.button2, buttons.button3 …?

My biggest mistake was thinking that you could simply write over an object with a new one, I had no idea another object was being created and the old one was still hanging around.

So what I’ve done is nil out the object before I create another one, for example.

local updateScore = function() display.remove(scGraphic) -- by adding these two lines scGraphic = nil score = score + 10 options = { ... } scGraphic = display.newText(options) mainGroup:insert( scGraphic ) end myTimer = timer.performWithDelay( 100, updateScore, 100)

Another concern I have is when I insert an object into the scene’s mainGroup…

mainGroup:insert( scGraphic )

Because it’s within a function and the function gets called a lot, is the scGraphic object being inserted into the mainGroup over and over? What is happening here?

Could you show me an example of what you mean by “make variable references upon the scene’s object rather than explicitly creating variables in the module.”?

Thanks for your input so far Horace, I’m quite prepared to re-write all my code if needs be.

You can actually write over one object’s variable reference and it will get garbage collected; It’s just not good practice because it leads to reading your own code badly and that leads to making mistakes.

For display object cleaning I create a named reference to a display group’s children on the group itself. That way, all I need to do is remove the group and set it’s own variable to nil. All the display objects are removed and their references in two lines.

Your scGraphic is not being inserted over and over because it is removed from display memory when you call display.remove(scGraphic) The variable reference needs to be set to nil, but you’re already doing that.

Ok, so as an example of how I write a scene, to keep everything tidy, here’s how I would write a button creation function:

-- file: buttonlib.lua local buttonlib = {} local function createButton( parent, title, x, y, callback ) parent = parent or display.currentStage local group = display.newGroup() group.class = "button" group.x, group.y = x, y parent:insert( group ) local text = display.newText{ parent=group, text=title, fontSize=32, x=0, y=0 } group.text = text local bg = display.newRoundedRect( group, 0, 0, text.width\*1.1, text.height\*1.2, 15 ) bg.fill = {0,1,0} function group:touch(e) return true end group:addEventListener( "touch", group ) function group:tap(e) if (callback) then callback(e) end return true end group:addEventListener( "tap", group ) return group end buttonlib.createButton = createButton return buttonlib

So, obviously, there’s a lot missing (decent colour settings, for one) but this demonstrates how I reference objects with other objects. I would never, ever keep a reference in another piece of code to the text object created inside this function. That would cause memory leaks but it is also unnecessary because, if I have a reference to this button I can just call it’s .text object.

Removing the parent group (assuming that the scene’s view is passed as the first parameter) will remove all display objects created within this function and, because the only variable references to those objects are within this function or stored against the ‘group’ variable, they are garbage collected too, when the scene’s view is removed.

I do not explicitly remove touch or tap listeners because they are local listeners. This is explained in the “important” block, here:

http://docs.coronalabs.com/daily/guide/events/detectEvents/index.html#registering-events

If I were to create a scene which uses this button, I would do something like this (missing lots of code here):

local buttonlib = require("buttonlib") local composer = require("composer") local scene = composer.newScene() local sceneGroup local function gotoMenu(e) composer.gotoScene( "menu", { time=350, effect="fade" } ) end function scene:create( event ) sceneGroup = self.view sceneGroup.aMenuButton = buttonlib.createButton( sceneGroup, "Go to Menu", 100, 100, gotoMenu ) end function scene:show( event ) if (event.phase == "will" ) then sceneGroup.aMenuButton.alpha = 0 elseif (event.phase == "did" ) then transition( sceneGroup.aMenuButton, { time=350, alpha=1 } ) end end function scene:hide( event ) if (event.phase == "will" ) then transition( sceneGroup.aMenuButton, { time=350, alpha=0 } ) elseif (event.phase == "did" ) then end end function scene:destroy( event ) sceneGroup = nil end scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) return scene

If I were listening to an object that I created myself, for example for custom events or events fired on the Runtime object, I would explicitly remove them in the hide() function’s “will” phase. If the createButton() function had any listeners for custom events it would benefit from having a “scene” parameter. It could then listen for the end of a scene and perform it’s own removeEventListener operations at the appropriate time.

I’m still using storyboard so it took me a while to understand your code.

What you are doing is adding a menu button to the composers create scene table, when the scene is destroyed so is the menu button along with it. Got it.

It never crossed my mind to do this before because in my create scene the sceneGroup = self.view is local and can’t be accessed in the enterScene (or screen:show in composer).

What you’ve done is scoped the sceneGroup outside of the create scene so it’s accessible throughout the entire Composer API.

This is what happens when you follow the Corona Docs literally, you get lead along a different path.

As you can see in the Corona Docs here: http://docs.coronalabs.com/api/library/composer/index.html#template

The template makes every sceneGroup local within each scene:

function scene:create( event ) local sceneGroup = self.view -- Initialize the scene here. -- Example: add display objects to "sceneGroup", add touch listeners, etc. end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- elseif ( phase == "did" ) then -- end end function scene:hide( event ) local sceneGroup = self.view

Surely if you follow their example and add objects to the sceneGroup in the function scene:create( event ) those objects are local to the create scene and can’t be accessed anywhere else?

This is what I’ve been doing.

What you’ve done is scoped the sceneGroup outside of the create scene so it’s accessible throughout the entire Composer API.

No; What I’ve done is scoped the display group of the scene to this scene’s module - not the whole composer API. For example, this scene’s group is not available to the menu scene.

The only major difference between their example and mine is that theirs retrieves the scene group in each function. The reason I don’t do that is so that I can have supporting functions in the same module which don’t need to take the scene group as a parameter. For example, the gotoMenu() function can still reference the scene’s group if it needs to. (Moving it out into a supporting library would break this tight coupling, of course.)

Sorry, my mistake, I didn’t mean the entire composer API, I was referring to the module.

Thanks for all you help, you’ve cleared up a lot of my concerns.

Yes. The loop is creating 4 transitions but you will only retain a variable reference to the last one. You could name them using tags (see the docs for this) or you could add them to a table, eg:

local myTransitions = {} for a=1, 4 do myTransitions[#myTransitions+1] = transition.to( spaceship[a], { time=2000, alpha=0, x=spX[a], y=spY[a] } ) end

I had a feeling that was the problem, I wish I knew this before I started making my game 9 months ago, it’s going to take me days to fix this, my code is massive and complex and I’ve got timers and transitions going on all over the place.

Thanks Horace

Sounds like the perfect time for some refactoring and unit testing.

Yes Horace,

I’ve been doing this for days, my code was very sloppy.

I’ve been making external modules to lean up the main chunks but I’m concerned that they are just adding to my memory leaks.

Following this… http://forums.coronalabs.com/topic/22091-guide-findingsolving-memory-leaks/

Has created this in just one of my 30 modules…

 buttons.passButton:removeEventListener("touch", buttons.passButton) buttons.button1:removeEventListener( "touch", buttons.button1 ) buttons.button2:removeEventListener( "touch", buttons.button2 ) buttons.button3:removeEventListener( "touch", buttons.button3 ) buttons.button4:removeEventListener( "touch", buttons.button4 ) if selftimer ~= nil then timer.cancel( selftimer ) selftimer = nil end if myTransition ~= nil then transition.cancel( myTransition ) myTransition = nil end display.remove(background) display.remove(cashSlider) display.remove(cashbackground) display.remove(letter1) display.remove(letter2) display.remove(letter3) display.remove(letter4) display.remove(textField1) if trueOrfalse == true then display.remove(textField2) display.remove(textField3) end display.remove(scGraphic) display.remove(questionCountGraphic) for i = 1, 20 do display.remove(coinSprite[i]) coinSprite[i] = nil end for i = 1, 10 do physics.removeBody( star[i], "dynamic") display.remove(star[i]) star[i] = nil end coinSprite = nil star = nil i = nil fontSheet = nil coinSheet = nil coinSprite = nil background = nil cashSlider = nil cashbackground = nil letter1 = nil letter2 = nil letter3 = nil letter4 = nil textField1 = nil textField2 = nil textField3 = nil textField4 = nil textField5 = nil questionCountGraphic = nil scGraphic = nil buttons.button1 = nil buttons.button2 = nil buttons.button3 = nil buttons.button4 = nil buttons.button5 = nil buttons.multiply5 = nil buttons.multiply4 = nil buttons.multiply3 = nil buttons.multiply2 = nil buttons.multiply1 = nil buttons.scoreCard = nil buttons.livesCard = nil buttons.passCard = nil buttons.passButton = nil buttons = nil audio.stop() audio.dispose(sfx.ticktock) audio.dispose(sfx.backSound) audio.dispose(sfx.fastSound) audio.dispose(sfx.passSound) audio.dispose(sfx.chinkSound) audio.dispose(sfx.wrongSound) audio.dispose(sfx.failSound) audio.dispose(sfx.coinSound) audio.dispose(sfx.furiousSound) audio.dispose(sfx.correctSound) audio.dispose(sfx.whooshSound) backSound = nil ticktockSound = nil wrongSound = nil chinkSound = nil passSound = nil correctSound = nil whooshSound = nil failSound = nil furiousSound = nil coinSound = nil sfx.ticktock = nil sfx.backSound = nil sfx.fastSound = nil sfx.passSound = nil sfx.chinkSound = nil sfx.wrongSound = nil sfx.failSound = nil sfx.coinSound = nil sfx.furiousSound = nil sfx.correctSound = nil sfx.whooshSound = nil sfx = nil collectgarbage( "collect" )

I’m quite sure I don’t need to do most of this because I declare everything local within the module, but I do it to make sure.

It seems quite mad that the guide says you have to remove all the display objects yourself?

Surely if the display object is inserted into the scene’s mainGroup it’s taken care of by the garbage disposal?

Also, disposing and nilling out all those sounds is just bloating my code.

And now I’ve got to rename all my Transitions and Timers, aarrgghh!

Back to work…

I find it better to make variable references upon the scene’s object rather than explicitly creating variables in the module. This is because they will be removed when the scene is cleaned up. You just need to make sure that what you think is being cleaned up actually is.

If you were to put all of those references into tables and then nil out the table (after removing the listeners) you would be making the cleanup easier.

Hi Horace,

Like what I’ve done with the buttons?

So if I just nil out the buttons table then all the objects in that table would be nilled out too?

buttons = nil would nil out buttons.button1, buttons.button2, buttons.button3 …?

My biggest mistake was thinking that you could simply write over an object with a new one, I had no idea another object was being created and the old one was still hanging around.

So what I’ve done is nil out the object before I create another one, for example.

local updateScore = function() display.remove(scGraphic) -- by adding these two lines scGraphic = nil score = score + 10 options = { ... } scGraphic = display.newText(options) mainGroup:insert( scGraphic ) end myTimer = timer.performWithDelay( 100, updateScore, 100)

Another concern I have is when I insert an object into the scene’s mainGroup…

mainGroup:insert( scGraphic )

Because it’s within a function and the function gets called a lot, is the scGraphic object being inserted into the mainGroup over and over? What is happening here?

Could you show me an example of what you mean by “make variable references upon the scene’s object rather than explicitly creating variables in the module.”?

Thanks for your input so far Horace, I’m quite prepared to re-write all my code if needs be.

You can actually write over one object’s variable reference and it will get garbage collected; It’s just not good practice because it leads to reading your own code badly and that leads to making mistakes.

For display object cleaning I create a named reference to a display group’s children on the group itself. That way, all I need to do is remove the group and set it’s own variable to nil. All the display objects are removed and their references in two lines.

Your scGraphic is not being inserted over and over because it is removed from display memory when you call display.remove(scGraphic) The variable reference needs to be set to nil, but you’re already doing that.

Ok, so as an example of how I write a scene, to keep everything tidy, here’s how I would write a button creation function:

-- file: buttonlib.lua local buttonlib = {} local function createButton( parent, title, x, y, callback ) parent = parent or display.currentStage local group = display.newGroup() group.class = "button" group.x, group.y = x, y parent:insert( group ) local text = display.newText{ parent=group, text=title, fontSize=32, x=0, y=0 } group.text = text local bg = display.newRoundedRect( group, 0, 0, text.width\*1.1, text.height\*1.2, 15 ) bg.fill = {0,1,0} function group:touch(e) return true end group:addEventListener( "touch", group ) function group:tap(e) if (callback) then callback(e) end return true end group:addEventListener( "tap", group ) return group end buttonlib.createButton = createButton return buttonlib

So, obviously, there’s a lot missing (decent colour settings, for one) but this demonstrates how I reference objects with other objects. I would never, ever keep a reference in another piece of code to the text object created inside this function. That would cause memory leaks but it is also unnecessary because, if I have a reference to this button I can just call it’s .text object.

Removing the parent group (assuming that the scene’s view is passed as the first parameter) will remove all display objects created within this function and, because the only variable references to those objects are within this function or stored against the ‘group’ variable, they are garbage collected too, when the scene’s view is removed.

I do not explicitly remove touch or tap listeners because they are local listeners. This is explained in the “important” block, here:

http://docs.coronalabs.com/daily/guide/events/detectEvents/index.html#registering-events

If I were to create a scene which uses this button, I would do something like this (missing lots of code here):

local buttonlib = require("buttonlib") local composer = require("composer") local scene = composer.newScene() local sceneGroup local function gotoMenu(e) composer.gotoScene( "menu", { time=350, effect="fade" } ) end function scene:create( event ) sceneGroup = self.view sceneGroup.aMenuButton = buttonlib.createButton( sceneGroup, "Go to Menu", 100, 100, gotoMenu ) end function scene:show( event ) if (event.phase == "will" ) then sceneGroup.aMenuButton.alpha = 0 elseif (event.phase == "did" ) then transition( sceneGroup.aMenuButton, { time=350, alpha=1 } ) end end function scene:hide( event ) if (event.phase == "will" ) then transition( sceneGroup.aMenuButton, { time=350, alpha=0 } ) elseif (event.phase == "did" ) then end end function scene:destroy( event ) sceneGroup = nil end scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) return scene

If I were listening to an object that I created myself, for example for custom events or events fired on the Runtime object, I would explicitly remove them in the hide() function’s “will” phase. If the createButton() function had any listeners for custom events it would benefit from having a “scene” parameter. It could then listen for the end of a scene and perform it’s own removeEventListener operations at the appropriate time.

I’m still using storyboard so it took me a while to understand your code.

What you are doing is adding a menu button to the composers create scene table, when the scene is destroyed so is the menu button along with it. Got it.

It never crossed my mind to do this before because in my create scene the sceneGroup = self.view is local and can’t be accessed in the enterScene (or screen:show in composer).

What you’ve done is scoped the sceneGroup outside of the create scene so it’s accessible throughout the entire Composer API.

This is what happens when you follow the Corona Docs literally, you get lead along a different path.

As you can see in the Corona Docs here: http://docs.coronalabs.com/api/library/composer/index.html#template

The template makes every sceneGroup local within each scene:

function scene:create( event ) local sceneGroup = self.view -- Initialize the scene here. -- Example: add display objects to "sceneGroup", add touch listeners, etc. end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- elseif ( phase == "did" ) then -- end end function scene:hide( event ) local sceneGroup = self.view

Surely if you follow their example and add objects to the sceneGroup in the function scene:create( event ) those objects are local to the create scene and can’t be accessed anywhere else?

This is what I’ve been doing.

What you’ve done is scoped the sceneGroup outside of the create scene so it’s accessible throughout the entire Composer API.

No; What I’ve done is scoped the display group of the scene to this scene’s module - not the whole composer API. For example, this scene’s group is not available to the menu scene.

The only major difference between their example and mine is that theirs retrieves the scene group in each function. The reason I don’t do that is so that I can have supporting functions in the same module which don’t need to take the scene group as a parameter. For example, the gotoMenu() function can still reference the scene’s group if it needs to. (Moving it out into a supporting library would break this tight coupling, of course.)