Removing Event Listeners Problem

I’m noticing certain bits going twice as fast when I return to them in my game, making me think certain (all?) enterFrames are triggering twice. Am I removing event listeners correctly? I have something like this:

local function mainmenuframe(event)

– do main menu stuff

end

local function refresh()
if(scene == “mainmenu”) then
Runtime:addEventListener(“enterFrame”, mainmenuframe)
else
Runtime:removeEventListener(“enterFrame”, mainmenuframe)
end
end

When I delete these removeEventListener calls for enterFrame, everything works exactly the same, making me think they’re not doing anything

Without seeing the rest of your code, I would recommend inputting a print statement to find out if it’s being called and the contents of the scene variable. You have the right syntax, but the if/then statement is the issue. I’m betting the print statements would give you better insight as to what is going on. 

print(scene) -- output scene variable contents if(scene == "mainmenu") then Runtime:addEventListener("enterFrame", mainmenuframe) else print('removed event listener') -- output a print statement to make sure it's being called Runtime:removeEventListener("enterFrame", mainmenuframe) end

I stuck some prints around and the if statements do seem to be functioning right, just the literal remove line that doesn’t work. I’ll try and make as simple a replication as I can when I get home

figured out the problem. somewhere in my 2000+ line main.lua (first corona project, it’s messy), there were calls to remove event listeners that came above when the functions were written. just written, there weren’t any calls to remove an event listener before it had been first “acknowledged”, and it had already been used in the game. it was just the writing order. seems like at the start of every frame and every “scan” of the code, a function has to be “rediscovered” in the order before it can be removed.

here’s a very simple code example of the problem:

-- moving state starts off as off local moving = false -- create a square in the top left local square = display.newRect( 0, 0, 100, 100 ) -- forward declare refresh. this is because touchsquare() at the top calls refresh(), but there may be things in refresh that call touchsquare, so it needs to be above local refresh -- when you touch the square, it reverses state. if reversing to false, remove the enterFrame listener for moving the square local function touchsquare(event) if ( event.phase == "began" ) then if(moving == false) then moving = true else moving = false Runtime:removeEventListener( "enterFrame", movesquare ) end refresh() end end -- add the event listener for this touching the square function square:addEventListener( "touch", touchsquare ) -- our enterframe function for moving the square local function movesquare() square.x = square.x + 1 end -- this is called when we touch the square. if moving has been turned to "true", we add the enterframe event listener function refresh() if(moving == true) then square.x = 0 Runtime:addEventListener( "enterFrame", movesquare ) end end -- this calls refresh at the very beginning, when the app first loads refresh()

note that each time you click on the square, it resets to x=0 but just keeps speeding up, because listeners are being duplicated for the enterFrame. 

moving the “touchsquare” function below “refresh” fixes it. but thing is, there might be important things in touchsquare that means it has to above certain other functions (it might be a really complicated enterFrame itself). so it seems like the order could become impossible (even if you forward declare things, which is the usual solution)

kinda frustrated and struggling to make sense of this in my head, but maybe things will click into place just fine if i keep this in mind from the start with future projects

edit: even simpler demo-

local square = display.newRect( 0, 0, 100, 100 ) local function touchsquare(event) if ( event.phase == "began" ) then print("touching but not removing") Runtime:removeEventListener( "enterFrame", movesquare ) end end square:addEventListener( "touch", touchsquare ) local function movesquare() square.x = square.x + 1 end Runtime:addEventListener( "enterFrame", movesquare )

I looked at your first sample here and this has to do with scope. Here’s the fixed code of your first sample:

-- moving state starts off as off local moving = false  -- create a square in the top left local square = display.newRect( 0, 0, 100, 100 ) -- forward declare refresh. this is because touchsquare() at the top calls refresh(), but there may be things in refresh that call touchsquare, so it needs to be above local refresh -- our enterframe function for moving the square local function movesquare() square.x = square.x + 1 end -- when you touch the square, it reverses state. if reversing to false, remove the enterFrame listener for moving the square local function touchsquare(event) if ( event.phase == "began" ) then if(moving == false) then moving = true else moving = false Runtime:removeEventListener( "enterFrame", movesquare ) end refresh() end end -- add the event listener for this touching the square function square:addEventListener( "touch", touchsquare ) -- this is called when we touch the square. if moving has been turned to "true", we add the enterframe event listener function refresh() if(moving == true) then square.x = 0 Runtime:addEventListener( "enterFrame", movesquare ) end end -- this calls refresh at the very beginning, when the app first loads refresh()

The function movesquare() was declared after the removeEventListener and that was causing problems for you. I moved this function above touchsquare() and now the code operates like expected. If you wanted to keep the function movesquare() where it is, you would need to make a forward reference to the variable name movesquare. Here’s how:

-- moving state starts off as off local moving = false  -- create a square in the top left local square = display.newRect( 0, 0, 100, 100 ) -- forward declare refresh. this is because touchsquare() at the top calls refresh(), but there may be things in refresh that call touchsquare, so it needs to be above local refresh local movesquare -- when you touch the square, it reverses state. if reversing to false, remove the enterFrame listener for moving the square local function touchsquare(event) if ( event.phase == "began" ) then if(moving == false) then moving = true else moving = false Runtime:removeEventListener( "enterFrame", movesquare ) end refresh() end end -- add the event listener for this touching the square function square:addEventListener( "touch", touchsquare ) -- our enterframe function for moving the square function movesquare() square.x = square.x + 1 end -- this is called when we touch the square. if moving has been turned to "true", we add the enterframe event listener function refresh() if(moving == true) then square.x = 0 Runtime:addEventListener( "enterFrame", movesquare ) end end -- this calls refresh at the very beginning, when the app first loads refresh()

In this example, I removed the word local function ‘local function movesquare’ and put a line above touchsquare() for ‘local movesquare’. Although old, there’s a great article here that covers some of the basics - https://coronalabs.com/blog/2011/09/21/tutorial-scopes-for-functions/.

Without seeing the rest of your code, I would recommend inputting a print statement to find out if it’s being called and the contents of the scene variable. You have the right syntax, but the if/then statement is the issue. I’m betting the print statements would give you better insight as to what is going on. 

print(scene) -- output scene variable contents if(scene == "mainmenu") then Runtime:addEventListener("enterFrame", mainmenuframe) else print('removed event listener') -- output a print statement to make sure it's being called Runtime:removeEventListener("enterFrame", mainmenuframe) end

I stuck some prints around and the if statements do seem to be functioning right, just the literal remove line that doesn’t work. I’ll try and make as simple a replication as I can when I get home

figured out the problem. somewhere in my 2000+ line main.lua (first corona project, it’s messy), there were calls to remove event listeners that came above when the functions were written. just written, there weren’t any calls to remove an event listener before it had been first “acknowledged”, and it had already been used in the game. it was just the writing order. seems like at the start of every frame and every “scan” of the code, a function has to be “rediscovered” in the order before it can be removed.

here’s a very simple code example of the problem:

-- moving state starts off as off local moving = false -- create a square in the top left local square = display.newRect( 0, 0, 100, 100 ) -- forward declare refresh. this is because touchsquare() at the top calls refresh(), but there may be things in refresh that call touchsquare, so it needs to be above local refresh -- when you touch the square, it reverses state. if reversing to false, remove the enterFrame listener for moving the square local function touchsquare(event) if ( event.phase == "began" ) then if(moving == false) then moving = true else moving = false Runtime:removeEventListener( "enterFrame", movesquare ) end refresh() end end -- add the event listener for this touching the square function square:addEventListener( "touch", touchsquare ) -- our enterframe function for moving the square local function movesquare() square.x = square.x + 1 end -- this is called when we touch the square. if moving has been turned to "true", we add the enterframe event listener function refresh() if(moving == true) then square.x = 0 Runtime:addEventListener( "enterFrame", movesquare ) end end -- this calls refresh at the very beginning, when the app first loads refresh()

note that each time you click on the square, it resets to x=0 but just keeps speeding up, because listeners are being duplicated for the enterFrame. 

moving the “touchsquare” function below “refresh” fixes it. but thing is, there might be important things in touchsquare that means it has to above certain other functions (it might be a really complicated enterFrame itself). so it seems like the order could become impossible (even if you forward declare things, which is the usual solution)

kinda frustrated and struggling to make sense of this in my head, but maybe things will click into place just fine if i keep this in mind from the start with future projects

edit: even simpler demo-

local square = display.newRect( 0, 0, 100, 100 ) local function touchsquare(event) if ( event.phase == "began" ) then print("touching but not removing") Runtime:removeEventListener( "enterFrame", movesquare ) end end square:addEventListener( "touch", touchsquare ) local function movesquare() square.x = square.x + 1 end Runtime:addEventListener( "enterFrame", movesquare )

I looked at your first sample here and this has to do with scope. Here’s the fixed code of your first sample:

-- moving state starts off as off local moving = false  -- create a square in the top left local square = display.newRect( 0, 0, 100, 100 ) -- forward declare refresh. this is because touchsquare() at the top calls refresh(), but there may be things in refresh that call touchsquare, so it needs to be above local refresh -- our enterframe function for moving the square local function movesquare() square.x = square.x + 1 end -- when you touch the square, it reverses state. if reversing to false, remove the enterFrame listener for moving the square local function touchsquare(event) if ( event.phase == "began" ) then if(moving == false) then moving = true else moving = false Runtime:removeEventListener( "enterFrame", movesquare ) end refresh() end end -- add the event listener for this touching the square function square:addEventListener( "touch", touchsquare ) -- this is called when we touch the square. if moving has been turned to "true", we add the enterframe event listener function refresh() if(moving == true) then square.x = 0 Runtime:addEventListener( "enterFrame", movesquare ) end end -- this calls refresh at the very beginning, when the app first loads refresh()

The function movesquare() was declared after the removeEventListener and that was causing problems for you. I moved this function above touchsquare() and now the code operates like expected. If you wanted to keep the function movesquare() where it is, you would need to make a forward reference to the variable name movesquare. Here’s how:

-- moving state starts off as off local moving = false  -- create a square in the top left local square = display.newRect( 0, 0, 100, 100 ) -- forward declare refresh. this is because touchsquare() at the top calls refresh(), but there may be things in refresh that call touchsquare, so it needs to be above local refresh local movesquare -- when you touch the square, it reverses state. if reversing to false, remove the enterFrame listener for moving the square local function touchsquare(event) if ( event.phase == "began" ) then if(moving == false) then moving = true else moving = false Runtime:removeEventListener( "enterFrame", movesquare ) end refresh() end end -- add the event listener for this touching the square function square:addEventListener( "touch", touchsquare ) -- our enterframe function for moving the square function movesquare() square.x = square.x + 1 end -- this is called when we touch the square. if moving has been turned to "true", we add the enterframe event listener function refresh() if(moving == true) then square.x = 0 Runtime:addEventListener( "enterFrame", movesquare ) end end -- this calls refresh at the very beginning, when the app first loads refresh()

In this example, I removed the word local function ‘local function movesquare’ and put a line above touchsquare() for ‘local movesquare’. Although old, there’s a great article here that covers some of the basics - https://coronalabs.com/blog/2011/09/21/tutorial-scopes-for-functions/.