Posted on by

Removing Objects Corona SDKRemoving objects and getting rid of unneeded variables may seem trivial, but it’s actually common question among Corona newcomers and veterans alike.

The potential consequences of doing this simple-but-important task incorrectly can lead to memory leaks, app slowdowns, and even crashes (which you don’t want, obviously).

Removing display objects

Corona display object (and many other types of objects) can be removed using the object:removeSelf() method. Calling the method is the easy part, however, because there is an actual visual representation of the object being removed at this point.

The misleading part is, just because the object is no longer on the screen and can no longer be used, the variable you used to store a reference to that object has a “skeleton table” that is left behind after the display object is removed. If you call object:removeSelf() and then print the variable, like so:


-- create the object
local object = display.newImage( "image.png" )

-- remove the object
object:removeSelf()

print( object )

You’ll see that the variable still has a table value. So since the variable is not empty, that means it’s still holding onto memory and will continue to do so until you explicitly “tell” Lua that you are no longer using the variable, and that it’s okay to clean it up.

You do so by simply assigning a value of nil to the variable. And that’s all there is to “removing” a variable.

In the object:removeSelf() documentation, in the examples section underneath the method call, you see the following:


object = nil

Keep in mind that you’ll want to do the same with any other “living” variables that reference the object that you’re removing as well. What I mean by “living” variables are either globals (that do not get cleaned up automatically), or local variables within scope that may be holding a reference to the object you intend to remove completely.

Local variable gotcha

I touched on it already, but local variables (that is, variables that are declared with the word local preceding the variable name), get cleaned up automatically at the end of their block of code.

What that does not mean, however, is that if you create an object within a small, isolated block of code, that the object will be removed at the end of function execution. Actually, this is a pretty bad situation as the object will remain, but the local variable will be cleared away and you’ll have no way to access the object (which hinders you from being able to remove it later on).

Here’s an example of that scenario:


local function create_and_abandon()
local outcast = display.newImage( "image.png" )
outcast.x, outcast.y = 160, 240
end

create_and_abandon()

In the above example, the aptly named function, create_and_abandon(), simply creates an object (with a local variable reference) and positions it. At the end of the function’s block, the outcast variable is no longer accessible, leaving an orphaned display object that can no longer be manipulated or removed.

To prevent this scenario from occurring, simply ensure the variable that you use as a reference when you create the object is accessible outside the function block. Here’s the previous example, modified to be non-crippling:


local outcast -- forward declaration

local function create_and_abandon()
outcast = display.newImage( "image.png" )
outcast.x, outcast.y = 160, 240
end

create_and_abandon()

-- we can now remove the object
outcast:removeSelf()
outcast = nil

In this example, our function no longer lives up to its name as we can now access the outcast variable from outside the function. Notice how we omitted the “local” in the line where the object is created? This means the variable will no longer be local to that block—it will either look for a ‘local’ forward declaration, or create a new global variable. In this case, thanks to the local forward declaration on the first line of the example, the outcast object is still local, just not to the block it was created in.

And that’s just about all you need to know about removing objects and variables in Corona! Don’t let the lack of subject matter fool you—the knowledge covered by this tutorial is very important, is often confusing to Corona newcomers, and can prevent some nasty hard-to-trace problems from occurring in your apps.

For extra assurance, see the Removing Objects Properly section in the Display Objects and Stage guide.


Posted by . Thanks for reading...

16 Responses to “Properly Removing Objects and Variables”

  1. BeyondtheTech

    I’m still guilty of creating local objects in a function. Thanks for the tip.

    I would like to add that if you’re using transitions and timers for a particular object, you should also assign it to a key in that table. That way, you can also cancel the timer or transition before you remove it.

    object = display.newImage( “box.png” )
    object.x, object.y = 160, 240
    object.trans = transition.from( object, { time = 10000, x = 0, alpha = 0 } )

    If you want to delete it before the transition is completed, you can quickly do it like this:

    if object.trans then transition.cancel( object.trans ) end
    display.remove( object ) — or object:removeSelf()
    object = nil

    Reply
  2. Ken Cardita

    Thanks for clearly explaining the how and why of object memory management in Lua and the Corona SDK.

    Could you comment on what the proper handling of object and variable memory management when you add a physics body to an image along with collision and event handlers to the object.

    thanks

    Ken
    Curved Light Solutions LLC

    Reply
  3. Patsky - Patryk K

    Thanks for the post.

    What if you return an object from the function that was declared as local only inside the function? (your first example)? ie:

    local function create_and_abandon()
    local outcast = display.newImage( “image.png” )
    outcast.x, outcast.y = 160, 240
    end

    local myObject = create_and_abandon()

    myObject:removeSelf()
    myObject = nil

    I understand that myObject will be removed. What about outcast object?

    Patsky – Patryk K

    Reply
  4. Patsky - Patryk K

    Apologies, I forgot to write RETURN – I should write:

    local function create_and_abandon()
    local outcast = display.newImage( “image.png” )
    outcast.x, outcast.y = 160, 240
    return outcast
    end

    local myObject = create_and_abandon()

    myObject:removeSelf()
    myObject = nil

    Thanks,
    Patsky – Patryk K

    Reply
    • John

      I’ve just started using the Storyboard library. What about the object cleanup when used winthin the Storyboard events?

      I forward declare all my object, then define them in “createScene”. Should I be removing them in “exitScene” or “destroyScene”?

      I may be moving back and forth from the same scenes, so I wasn’t sure where/if I should be removing the objects at all.

      Thanks,
      - John

      Reply
      • Jonathan Beebe

        @John: It’s really up to you. When an entire display group is removed (as is the case when storyboard purges a scene), you don’t need to worry about calling removeSelf() on each individual object. If the variables are local to your scene module, then you don’t even need to worry about setting the variables to nil in your exitScene or destroyScene event listeners either.

        Reply
        • Tim

          Hey Jonathan, what different things would cause a storyboard.removeScene(“nameofscene”) to crash the simulator?

          I’m building an app for android with 2 scenes. The first scene pulls in some data from sqlite and gives you a list. You click one of the entries, and it loads the other scene with data related to that entry. Those 2 things work. BUT, if I click to go back to the entry list, the simulator locks up.

          The method I’m using is to removeScene completely when switching between the 2 scenes. It was working at one point, but I can’t tell what changed. In fact, I don’t recall changing anything (of course I must have right?).

          If I take out the removeScene(“2ndscene”) in the enterscene of the 1st scene, it loads that first scene again just fine, but then I can’t go back to the 2nd scene with anything actual visible on the screen (probably because everything is loading inside createScene and not getting ran at this point).

          Another confusing thing about this is I’m using another “side” scene hooked to that 2nd scene to force a reload of changed images. When an image is changed in the UI and saved, it goes to the other scene with nothing in it but a removeScene(“2ndscene”) then back to the 2nd scene. It’s a hack to force relocation of texture memory. That works fine and doesn’t lock up??!! I can’t do the same to the first scene, but I can to this side scene, and it makes no sense.

          So, again, the question is; what about the wiring in storyboard would cause it to lock up the sim on a removeScene?

          Related question. You say you don’t have to nil out stuff using storyboard. What about closing databases between scenes?

          I know this is kinda long. thanks for looking.

          Tim

          Reply
    • Jonathan Beebe

      @Patsky: In the case of your example, the ‘outcast’ variable is local to the create_and_abandon() function block, so the variable is cleared after that (though the returned object remains). You alleviate the issue by storing a reference to the object in another variable, myObject, so the problem described by my first example is not an issue.

      In your example, ‘outcast’ (the variable) is no longer accessible because it is auto-cleaned at the end of its block. The problem would be, if you didn’t return it at the end of the function and store its returned value into another variable (which you did do), then you’d have a display object with no reference (which means no way to remove it–unless you had a reference to it through its parent group).

      Your example is completely valid, and in fact, is a great replacement for my second example.

      Reply
  5. Chris Leyton

    Well Jon – you really threw me a curve ball here.

    Can I just ask whether this would be a suitable way, as I am creating localised display objects within a function.

    local function moveClouds()
    btnGroup.trans = transition.to(btnGroup, {time=500, x = _W, transition=easing.outExpo, onComplete=
    function()
    if btnGroup.trans then
    transition.cancel(btnGroup.trans)
    btnGroup.trans = nil
    end

    local emailImg = display.newImageRect(“email.png”, 297, 147)
    emailImg.x = _W/2
    emailImg.y = 200
    localGroup:insert(emailImg)

    local backBtn = display.newImageRect(“backCloud.png”, 41, 38)
    backBtn:setReferencePoint(display.BottomLeftReferencePoint)
    backBtn.x = 10
    backBtn.y = _H-10
    localGroup:insert(backBtn)

    backBtn:addEventListener(“touch”,
    function()
    print(“HERE”)
    emailImg:removeSelf()
    emailImg = nil
    btnGroup.trans = transition.to(btnGroup, {time=500, x = 0, transition=easing.outExpo, onComplete=
    function()
    if btnGroup.trans then
    transition.cancel(btnGroup.trans)
    btnGroup.trans = nil
    end

    if backBtn then
    backBtn:removeSelf()
    backBtn = nil
    end
    end})
    end)

    end})
    end

    Reply
  6. Gracille

    does anyone know a sample quiz game in corona? we just badly needed it for references for our thesis educational game…please help

    Reply
  7. Tim

    Jonathan, update on my previous questions about what would crash the sim. I found the problem. It was a listener that wasn’t letting go even tho I told it to. I went through and nil-ed everything and moved the location of my removeEvent and it fixed it.

    I’m still wondering about whether or not I should close and nil a database before moving on to another scene tho.

    Reply
  8. Daniel Sefton

    Hello,

    I have another similar issue that I’m debugging through. If you have an array of targets, say 6, and you ‘shoot’ and delete one, say number 3, the numChildren decrements to 5 as espected, but I think the array element for the original target[6] is still [6] and not [5]. In other words, the array is not packed. Is this correct?

    What it means is if you run a routine that updates stuff on the 6 targets, you have to do something like this:

    for i=1, originalTargetCount (not targets.numChildren which is decremented),1 do
    if target[i]
    DoSomething()
    end
    end

    Am I correct?

    Thanks!
    Daniel

    Reply
  9. kumar ks

    Hi,
    I am providing drag and drop option to a image by creating multiple copies of the same image. So when i place multiple copies of same image, at a same location , when i provide remove method, it only removes only the last image which i placed . So what i have to do to remove all the images at the same place. Sp provide me some solution.

    Reply
  10. bava

    Hai all !!!

    i am newbie to programming and especially to corona sdk (LUA).i need a help!
    The problem is :
    I have 10 images in the array and a button while tap the button i need to remove the previous image and show the next image stored in array.
    I did all ,but while tap for next image the next image is comes nicely, but the previous image is not removed from screen,
    i want to remove it.,
    and one more thing is after completing 10th image , i like to start from image 1,like a loop.

    Thats it!!!

    Any help regarding this will be great to me.,
    thanks a lot in advance!!!

    My code is here:

    local Next = function()
    for j = 1, 10 do
    j=j+1
    end
    return true
    end

    local dotted = { “images/1.png”, “images/2.png” ,”images/3.png”,”images/4.png”,”images/5.png”,
    “images/6.png”,”images/7.png”,”images/8.png”,”images/9.png”,”images/10.png”
    }

    local nextButton = widget.newButton{
    left = display.contentWidth/1.25,
    top = display.contentHeight – 55,
    defaultFile=”images/next.png”,
    width = 50, height = 50,
    onRelease = Next
    }

    j = 1
    function loadingImages1()
    di = display.newImageRect(dotted[j],150,300);
    di.x = calcx(40,”PER”)
    di.y = calcx(30,”PER”)
    di.height = calch(60,”PER”)
    di.width = calcw(20,”PER”)
    j = j + 1
    end

    local function onObjectTap( self,event )
    –di1.removeSelf();
    di1:removeSelf();
    loadingImages1()
    return true
    end
    nextButton:addEventListener( “tap”, onObjectTap )

    Reply

Leave a Reply

  • (Will Not Be Published)