memory leak with touch listener

I’m getting a memory leak when I use a touch listener in my code.

  
local rnd = math.random  
local absolute = math.abs  
local score = 0  
local check = 0  
  
function addBall()  
  
 destroy = display.newRect( rnd(500), -150, 125, 125 )  
 destroy:setFillColor(255, 0, 0 )  
  
 collectgarbage("collect")  
 print("System Memory : "..collectgarbage("count"))  
 print("addball")  
  
 --transition.to( destroy, { time=1900, alpha=1, x=destroy.x, y=display.viewableContentHeight + 200 } )  
  
 function removeBall(obj)  
  
 obj:removeSelf()  
 obj = nil  
  
 addBall()  
  
 end  
  
 transition.to( destroy, { time=100, alpha=1, x=destroy.x, y=display.viewableContentHeight + 200, onComplete=removeBall } )  
  
 local function ballTouch(touch)  
  
  
 collectgarbage("collect")  
 print("System Memory : "..collectgarbage("count"))  
 print("balltouch")  
 print("removeBall")  
  
 removeBall(destroy)  
  
 end  
  
 destroy:addEventListener("touch", ballTouch)  
  
 collectgarbage("collect")  
 print("System Memory : "..collectgarbage("count"))  
 print("addlistener")  
  
end  
  
local timerBall = timer.performWithDelay(1000, addBall, 1)  
  

If you run the code as is there’s no memory leak. But if you comment out the current transition and uncomment the other transition the code starts leaking.

One uses a touch listener and it leaks, the other does not.

This code is best viewed on the iphone 4 hardware.

Any thoughts on using the touch listener so that I don’t get a leak? Thanks as always in advance. [import]uid: 10903 topic_id: 4743 reply_id: 304743[/import]

Is anyone able to recreate this? Should be easy with the code I offered. [import]uid: 10903 topic_id: 4743 reply_id: 15264[/import]

There seems to be a few “odd” things in this code, just looking at a quick glance… I haven’t looked into it thoroughly.

For one thing, why are the “removeBall” and “ballTouch” functions nested within the “addBall”’ function? Personally, I don’t program this way… I’m not saying it’s bad or invalid, but I’m coming from TorqueScript which didn’t seem to like nested functions. As such, I just avoided doing it and the habit stuck. I know that it’s totally valid and sometimes necessary in Lua, but in this case, I see no reason for it.

If I get what you’re aiming for here, the Timer at the very bottom spits out 1 ball after 1000 milliseconds (there doesn’t appear to be any ball produced in this code, but that’s not important to this post). What is produced is a rectangle “destroy”. This rectangle is applied a Listener which, when touched, calls the function “ballTouch”. When “ballTouch” is called, it calls yet another function “removeBall”. Then… after all of this… it calls “addBall” again…

One possible issue is that (I believe this is true) you can have multiple Listeners of the same name. You must clean up and manage your Listeners… it sounds obvious, but alot of people forget! :slight_smile: When you call “allBall” here, you’re adding the same touch Listener over and over, but you never remove this Listener. Managing these Listeners is a key aspect of clean Lua code. If you have an object with a Listener, and it gets destroyed, you MUST destroy the Listener or it will forever remain in memory. If you retain the same object in memory, the Listener can remain “attached” to it, but once it goes, the Listener must be forcibly removed too.

I would also definitely break those nested functions out. Put them both above and outside of the “allBall” function and make them both local. Instead of producing a new “destroy” rectangle each time, declare just one in the beginning of your code (it can be located at 0,0 and have a size of 0,0 initially). Apply 1 touch Listener to it immediately after. Have the initial Timer “re-locate” the rectangle in the allBall function, using random math. When the rectangle is clicked, the theoretical ball (which I assume you’ll add later) gets destroyed. The rectangle then moves to a new location, but the same Listener remains attached. When you’re done with this whole routine, delete (in this order!) the Listener, the object (using removeSelf), then “nil” it out.

That should do it… obviously I haven’t tested it or considered every angle, but try doing some of this and see where it leads.

Best of luck!
Brent
[import]uid: 9747 topic_id: 4743 reply_id: 15322[/import]

Thanks again Brent.

Here’s the code un-nested, and tweaked. What I ended up doing was not removing the rectangle at all but just resetting it’s position so that it seems like it’s been removed.

[code]

local rnd = math.random
local absolute = math.abs
local score = 0
local check = 0
local rect

function resetRect(rect)

transition.to( rect, { time=1900, alpha=1, x=rect.x, y=display.viewableContentHeight + 200 } )

end

function removeRect(rect)

transition.to( rect, { time=1, alpha=1, x=rnd(500), y=-150, onComplete=resetRect } )

end

local function rectTouch(touch)

collectgarbage(“collect”)
print("System Memory : "…collectgarbage(“count”))
print(“balltouch”)
print(“removeBall”)

removeRect(rect)

end

function addRect()

rect = display.newRect( rnd(500), -150, 125, 125 )
rect:setFillColor(255, 0, 0 )

collectgarbage(“collect”)
print("System Memory : "…collectgarbage(“count”))
print(“addball”)

transition.to( rect, { time=1900, alpha=1, x=rect.x, y=display.viewableContentHeight + 200 } )

–transition.to( rect, { time=100, alpha=1, x=rect.x, y=display.viewableContentHeight + 200, onComplete=removeRect } )

rect:addEventListener(“touch”, rectTouch)

collectgarbage(“collect”)
print("System Memory : "…collectgarbage(“count”))
print(“addlistener”)

end

local timerBall = timer.performWithDelay(1000, addRect, 1)

[/code] [import]uid: 10903 topic_id: 4743 reply_id: 15328[/import]

That looks much better! Does it work? Are you still getting memory leaks?

A few more cleaning tweaks:

  1. You can eliminate the “removeRect” function and combine it with “rectTouch”. Just take the lone transition command and put it down in the “rectTouch” function. Remove the call to “removeRect” since that will be gone. In fact, that doesn’t even need to be a transition with a 1-millisecond timer. Just reposition the rectangle immediately, then call “resetRect” without using the transition/onComplete:
local function rectTouch(touch)  
  
 collectgarbage("collect")  
 print("System Memory : "..collectgarbage("count"))  
 print("balltouch")  
 print("removeBall")  
  
 rect.x = rnd(500) ; rect.y = -150 ; rect.alpha = 1  
 resetRect()  
  
end  
  1. The “resetRect” function can be local, since nothing from above references it (one of the most tricky things for me when starting Lua was organizing my local functions properly in the top-down order, so all those above were only referenced by those below… or else making pre-declarations above, but that would be a topic for another post). [import]uid: 9747 topic_id: 4743 reply_id: 15350[/import]

No memory leak, which I’m incredibly happy about. It’s also much simpler based on your suggestions.

The local/global function difference is crazy confusing. I assumed it had to be global as I was passing rect between the functions.

[code]

local rnd = math.random
local score = 0
local rect

local function resetRect(rect)

transition.to( rect, { time=1900, alpha=1, x=rect.x, y=display.viewableContentHeight + 200 } )

end

local function rectTouch(touch)

collectgarbage(“collect”)
print("System Memory : "…collectgarbage(“count”))
print(“balltouch”)
print(“removeBall”)

rect.x = rnd(500) ; rect.y = -150 ; rect.alpha = 1
resetRect(rect)

end

function addRect()

rect = display.newRect( rnd(500), -150, 125, 125 )
rect:setFillColor(255, 0, 0 )

collectgarbage(“collect”)
print("System Memory : "…collectgarbage(“count”))
print(“addball”)

transition.to( rect, { time=1900, alpha=1, x=rect.x, y=display.viewableContentHeight + 200 } )

rect:addEventListener(“touch”, rectTouch)

collectgarbage(“collect”)
print("System Memory : "…collectgarbage(“count”))
print(“addlistener”)

end

local timerBall = timer.performWithDelay(1000, addRect, 1)

[/code] [import]uid: 10903 topic_id: 4743 reply_id: 15373[/import]