Modules and memory management.

Merry Christmas All,

I was just wondering what I need to do with modules concerning memory management.  I am using storyboards and just wanted to know if I need to nil things out or remove Event Listeners?

For example in the generic back button example below, when the button is pressed do I need to remove the listener?  What about the graphics? Or are these automatically cleaned up when the storyboard scene transitions off?  My worry is that when I use the same module on the next scene that this ‘copy’ is left hanging around and could be causing a memory leak…   This is a very simple example,  but I have some modules that are rather big, and could cause memory issues if they are left hanging around.

Thanks in advance, and again Merry Christmas…

Craig

main.lua (from enter scene)

[lua]

local utilitiesClass = require ( “Classes.utilities” )

local backButton = utilitiesClass:backButton()

Group:insert(backButton)

[/lua]

utilities.lua

[lua]

local utilities = {}

function utilities:tap(event)

    if event.target.id == “backButton” then

        local lastScene = storyboard.returnTo

        print( “previous scene”, lastScene )

        if ( lastScene ) then

            storyboard.gotoScene( lastScene, { effect=“fade”, time=500 , params = event.target.params} )

        end

    end

end

function utilities:backButton(params)

    self.backButtonIcon = display.newImageRect( “Images/backButton.png”, 80, 80 )

    if params then

        self.backButtonIcon.params = params

    else

        self.backButtonIcon.params = nil

    end

    self.backButtonIcon.id = “backButton”

    self.backButtonIcon:addEventListener(“tap”, self)

    return self.backButtonIcon

end

return utilities

[/lua]

I care a great deal about this.

Each of my scenes has at least 1 dedicated spritesheet, 3 event listeners (at least) and a common structure.

I depend on a specific function for unloading graphic sheets.

function unrequire(m) package.loaded[m] = nil rawset(\_G, m, nil) -- Search for the shared library handle in the registry and erase it local registry = debug.getregistry() local nMatches, mKey, mt = 0, nil, registry['\_LOADLIB'] for key, ud in pairs(registry) do if type(key) == 'string' and type(ud) == 'userdata' and getmetatable(ud) == mt and string.find(key, "LOADLIB: .\*" .. m) then nMatches = nMatches + 1 if nMatches \> 1 then return false, "More than one possible key for module '" .. m .. "'. Can't decide which one to erase." end mKey = key end end if mKey then registry[mKey] = nil end return true end

Scene files all look like this (mostly):

local scene = storyboard.newScene() local \_someoption -- example passed parameter local \_Nfo = require("images.sprites.loading") local \_Sheet = graphics.newImageSheet( "images/sprites/loading.png", \_Nfo:getSheet() ) local scene:createScene(event)     if event.params then         if event.params.someoption then             \_someoption = event.params.someoption         end     end end local scene:enterScene(event) end local scene:destroyScene(event) scene:removeEventListener( "createScene", scene ) scene:removeEventListener( "enterScene", scene ) scene:removeEventListener( "destroyScene", scene ) unrequire("images.sprites.loading") \_Sheet = nil end scene:addEventListener( "createScene", scene ) scene:addEventListener( "enterScene", scene ) scene:addEventListener( "destroyScene", scene ) return scene

When navigating backward and forward, I am removing old scenes from memory, triggering their destroyScene methods.

-- storyboard.removeScene("oldscenestring") -- storyboard.loadScene( "somescenestring", true) -- Need this for a specific case -- storyboard.gotoScene( "newscenestring", options) -- My Actual code looks like this: if toScene == fromScene then storyboard.removeScene("scenes."..fromScene) storyboard.loadScene( "scenes."..toScene, true) -- if navigating to new version of the SAME scene, we have to preload storyboard.gotoScene( "scenes."..toScene, options) -- BUG! Options do not get passed when navigating to same scene else storyboard.gotoScene( "scenes."..toScene, options) storyboard.removeScene("scenes."..fromScene) end

When navigating to new scenes, I’m instantiating scenes from scratch and passing arguments that are handled in createScene. You can see that if you reuse an existing scene (by name) the options will not be available. Corona has not responded to the bug I filed. I use a global variable to store options and read them out, in this rare case or use MyScene1 and MyScene2 and switch between them as I navigate. Icky.

This structure will keep your scenes memory neutral (after they are first used) through countless navigations.

I care a great deal about this.

Each of my scenes has at least 1 dedicated spritesheet, 3 event listeners (at least) and a common structure.

I depend on a specific function for unloading graphic sheets.

function unrequire(m) package.loaded[m] = nil rawset(\_G, m, nil) -- Search for the shared library handle in the registry and erase it local registry = debug.getregistry() local nMatches, mKey, mt = 0, nil, registry['\_LOADLIB'] for key, ud in pairs(registry) do if type(key) == 'string' and type(ud) == 'userdata' and getmetatable(ud) == mt and string.find(key, "LOADLIB: .\*" .. m) then nMatches = nMatches + 1 if nMatches \> 1 then return false, "More than one possible key for module '" .. m .. "'. Can't decide which one to erase." end mKey = key end end if mKey then registry[mKey] = nil end return true end

Scene files all look like this (mostly):

local scene = storyboard.newScene() local \_someoption -- example passed parameter local \_Nfo = require("images.sprites.loading") local \_Sheet = graphics.newImageSheet( "images/sprites/loading.png", \_Nfo:getSheet() ) local scene:createScene(event)     if event.params then         if event.params.someoption then             \_someoption = event.params.someoption         end     end end local scene:enterScene(event) end local scene:destroyScene(event) scene:removeEventListener( "createScene", scene ) scene:removeEventListener( "enterScene", scene ) scene:removeEventListener( "destroyScene", scene ) unrequire("images.sprites.loading") \_Sheet = nil end scene:addEventListener( "createScene", scene ) scene:addEventListener( "enterScene", scene ) scene:addEventListener( "destroyScene", scene ) return scene

When navigating backward and forward, I am removing old scenes from memory, triggering their destroyScene methods.

-- storyboard.removeScene("oldscenestring") -- storyboard.loadScene( "somescenestring", true) -- Need this for a specific case -- storyboard.gotoScene( "newscenestring", options) -- My Actual code looks like this: if toScene == fromScene then storyboard.removeScene("scenes."..fromScene) storyboard.loadScene( "scenes."..toScene, true) -- if navigating to new version of the SAME scene, we have to preload storyboard.gotoScene( "scenes."..toScene, options) -- BUG! Options do not get passed when navigating to same scene else storyboard.gotoScene( "scenes."..toScene, options) storyboard.removeScene("scenes."..fromScene) end

When navigating to new scenes, I’m instantiating scenes from scratch and passing arguments that are handled in createScene. You can see that if you reuse an existing scene (by name) the options will not be available. Corona has not responded to the bug I filed. I use a global variable to store options and read them out, in this rare case or use MyScene1 and MyScene2 and switch between them as I navigate. Icky.

This structure will keep your scenes memory neutral (after they are first used) through countless navigations.