How do enterframe and timer interrupts work?

Please don’t get the impression that objects just hang around after being deleted.  They are deleted right away.  The problem is, references to them are not.

You can get into trouble if you delete an object that will be touched later in the same frame.

Let me give you a ‘for example’

  1. A new frame starts.

  2. Phase A executes and a number of collision listeners are queued for ‘handling’.

  3. Phase B begins and as the listeners are called, one after the other, a listener deletes a display object X.

  4. Phase B continues and later another listener is processed, but this listener tries to do something with object X.

At this point, if you don’t have safety code in place, the app will crash or at least complain loudly. :slight_smile:

Question

What is the  table of objects for?

If you are keeping objects in a table and iterating over that table, but don’t care about order, then you’re better off doing this:

local myStuff = {} local function finalize( self ) myStuff[self] = nil end local first for i = 1, 10 do local ball = display.newCircle( 10, 10, 10 ) myStuff[ball] = ball first = first or ball -- grab reference to first ball for example end display.remove(first) -- 'finalize' automatically called and reference to ball removed from table. for k,v in pairs(myStuff) do v.x = v.x + 10 end

I like to fool / convince myself that my code is tight enough that once the object is marked for deletion no amount of queued listeners can effect it because it is locked.  Hence, the isEaten flag.  

I can’t remember why I started using a table which describes the display object.  I remember a couple years ago trying to add something to the display object and I couldn’t figure out how so I went with the table wrapped around the display object as a work around.  Now I have objects that transform into other objects so the master object is not deleted.  I delete the display object or swap it with other ones.   Think of a guy turning into super guy and back again.  The master table holds all the fields that describe the object and the functions that make it go.  I also have objects that are made up of multiple display objects.  The master table keeps them all correctly ordered.  

I don’t have a master table of objects that are on the screen.  I just loop through a displayGroup and see what is there.  If it is there then I call code to make it do things.   So if the self.image.removeSelf() doesn’t actually remove the object immediately I think I can get into this strange case.  Now if it does then I am just chasing shadows.  For now I am going to just lock the master object table and schedule it to be deleted a few frames later and pray.   Something like this:  

--- Clears this object. function FF:clear() for k, v in pairs( self ) do if type( k ) ~= "function" then self[k] = nil end end return end function FF:clearnow() self:clear() self = nil return end -- delete this object function FF:destroy() self.isEaten = true self.FGFgroup.currentcount = self.FGFgroup.currentcount - 1 if self.image.collision ~= nil then self.image:removeEventListener( "collision", collision\_fish ) end self.image:removeEventListener( "sprite", spritelistener ) transition.cancel(self.image) self.image:removeSelf() -- delete Display Object and removes from DG local myClosure = function() return FF.clearnow( self ) end timer.performWithDelay( 350, myClosure , 1 ) -- self:clear() -- self = nil return end

Corona is easy they said, It is 10 times faster than native apps they said.   I think this stuff is plenty complex.  

I want to say this in the nicest way I can, but “You’re making it more complex than it needs to be.”

Until you’ve made half a dozen or more games (I mean small ones) and until you’ve learned to use Lua and Corona  you’re simply going to run into these issues.

If you find yourself needing to make a convoluted or complex solution to solve a problem that you think should be simple, stop and re-factor.  Focus on the problem you’re trying avoid and ask yourself why it needs to be avoided.  What is causing it?

Blasting through and trying to make a complicated game from zero or little experience will be very hard.

In this example, the use of a proxy object with a reference to the display object is troublesome to me.  It opens an avenue for object management problems.

I’d be interested in seeing your game’s code (if it isn’t terribly long), or at least I’d like to know what the game is about and does.  

Again, I feel like you’re over-complicating things.  This is natural and a side-effect of lack of experience with Lua and Corona. 

We solve the problems we understand with the tools we have at hand.

PS - How many games and sample programs have you made with with Corona prior to starting this project?

PPS - Coding games in Corona is a superior experience to native.  :slight_smile:

Preface: I am not trying to pick on you, but I see signs that things are going wrong for you in the code so I thought I’d point out a few things.


Hi.  I am looking at your code post (your most recent one) and have some requests, questions, and suggestions.

<u><strong>Request</strong> </u>

When posting code, please convert all tabs to 3 spaces.&nbsp; This makes it easier to read in forums.&nbsp; Tabs are terrible in the code blocks here.&nbsp; :blink:

<u><strong>Question</strong></u>

When I look at this code:

    function FF:clear() for k, v in pairs( self ) do if type( k ) ~= "function" then self[k] = nil end end return end

I read it as:

- object ff has a method called clear()
- clear(), when called will iterate over every iterate-able field (Tip: not all fields will be iterated) and if the field <u><strong>name</strong></u>&nbsp;(the key k) is NOT a function then the reference will be cleared.

First, why are you trying to do this?

Second, this is wrong for (at least) two reasons:

1. As noted above, not all fields are iteratable so you will miss many.&nbsp; This is a weird thing and I can't exacltly explain it, but trust me you will miss all or most of the functions added by Corona.&nbsp; Only fields you added manually will iterate.
2. For this to work, you need to look at the type of the object the key is referring to, not the key.

     if type( v ) ~= "function" then self[k] = nil end

**This is one of those convoluted things that should make you stop and say... hmm why do I need to do this.**

<u><strong>No Need To Remove Local Listeners If...</strong></u>

I noticed in you remove the collision listener from and object.&nbsp; You don't need to do that if you're going to delete the object.

When you remove a display object ALL local listeners are automatically removed.&nbsp; i.e. If you added it like this:

    obj.touch = function( self, event ) end obj:addEventListener("touch")

It gets removed automatically when the object is deleteted.

<u><strong>Stopping Listeners Won't Stop Them From Being Called If They Are Queued</strong></u>

What?&nbsp;&nbsp;

What I'm saying is, if you try to stop listening for a "collision" listener on object X and object X is about the be processed for a collision, it will still get get called.

If the function is queued to be called in this frame it is going to be called no matter what you do. You need to handle this in the listener or prevent it from occurring at all through your design.&nbsp; (Tip: You can't always prevent this.)

<u><strong>No need to make a closure variable, and no need to 1 count. </strong></u>

This:

    timer.performWithDelay( 350, function() return FF.clearnow( self ) end )

is better than this:

    local closure = function() return FF.clearnow( self ) end timer.performWithDelay( 350, closure, 1 )

<u><strong>Dot dot dot dot... Equals Trouble</strong></u>

When I see code with lots of dots I know the developer is in trouble.&nbsp; To be fair, you're only two deep in this code, but it still smells of complicated and dangerous to me.

    self.FGFgroup.currentcount 

I was going to add some more notes... but an emergency came up over here and I have to jet.&nbsp; Sorry! :(

As always, good food for thought.  I am sure I am doing everything wrong.  I had never coded any game before this one.  So I am sure I am not doing things the best way.  some of my reasons are:  

1.  I have read over and over that if you don’t set variables to nil then they wont be garbage collected by lua.  That is why I was doing the clear.  Not sure why I am omitting the functions and now that you mention that I am omitting the functions incorrectly and they to are getting cleared so be it.  I guess they need to be cleared as well  :)      Is it true that if I just nil the parent of the table all the dot dot dot children will go away as well?   If that is true then I guess I don’t have to clear each field.   I have never had a lua class so I don’t know the answer to simple questions like this sadly.  

2.  As for the listeners I didn’t want them firing after the object was cleared.  That makes for errors a plenty.  I am sure at some point I got some error thrown at me so I just stuck that in there so they stop firing.  I bet I used a function listener once and that is why I got the error.  I guess I don’t need to do this for the table listeners.  But why not be thorough?   My thinking was if I created it I am deleting it.   

  1. I can get rid of the closure sure.  But to be fair.  Today I went to the corona web page which describes timer.performWithDelay and that is the example exactly as it was on the page.   As for letting it default to 1 instead of listing it?   Well what if someone changes the default to 9 in the next build?   You can’t be too careful.  

4.  I believe I have only added one more level of dots to the equation.  The master object which all the other stuff is a part of.  Otherwise, I would have had to add all these functions and fields directly to the display object.  I know you will hate this but some of my master objects inherit functions and fields from other master objects.  I have base fish functions FF.  special types of fish will use FF as the base and build on top of that so I don’t have to maintain mostly duplicate code for multiple types of fish.  The FGFgroup I created to implement schools of fish that swim together in patterns.  All the master objects in a school need to know which other ones are in their school.   As always, I am sure there was a much easier way to do things but I am just here with my dog and he doesn’t help much other than moral support.  

"What I’m saying is, if you try to stop listening for a “collision” listener on object X and object X is about the be processed for a collision, it will still get get called."

Now this is very interesting and important.  I could see this causing my problem.   Let’s say the enterframe queues my code and then a collision event is also queued.  I could see them firing back to back in just the right situations and the first one causing me to delete the master object.  Before, the dust settles the second event drives the code and something wasn’t completely cleaned up yet.  I can’t check for the master object to be locked because it is gone because it was just deleted.   Now as I type this I feel that if this situation happened I would get all sorts of strange errors and behavior.  But I have never seen any of that.   I have just seen the one case I described at the very top of the topic where I check for nil and the very next line the variable is nil.  That is one tight timing window.  

It seems that waiting 350 before deleting the master object should close this window.  But this assumes all my assumptions about what is going wrong are correct.   

Thanks again for the insight.  I know it takes a ton of time to help others and I really appreciate it.  

L

Answers to last post questions in order:
 
1. Yes, it is true.

local his = {} his.face = display.newCircle( 100, 100, 100 ) his.leye = display.newCircle( 80, 70, 10 ) his.reye = display.newCircle( 120, 70, 10 ) his.mouth = display.newLine( 70, 70, 140, 70) his.face:setfillColor( 0 ) his.leye:setfillColor( 0 ) his.reye:setfillColor( 0 ) his.mouth:setfillColor( 0 ) his.timer = function() for k,v in pairs( tmp ) do display.remove(v) end tmp = nil end timer.peformWithDelay( 1000, his )

  
One second later,  his face and face-parts is deleted…then all they all get garbage collected.  Ouch!

2. Sure it is OK to remove any listener you added, just so you know that for local event table listeners, they get removed automatically.

Runtime event listeners of all sorts must still be removed manually.

3. That will simply never happen and if it did, it would be a bug because someone changed the long-established API in a significant and unnecessary way.  :slight_smile:

4. The easiest way to protect yourself in this scenario is either to check for valid objects before operating on them, or far simpler add a flag to the object marking it valid or invalid.  Also, if you always add finalize listeners to clean up you’ll be golden.

For example:

local bob = display.newCircle( 100, 100, 100 ) -- The flag bob.isAlive = true function bob.enterFrame( self ) if( not self.isAlive ) then return end -- Do something to bob here end Runtime:addEventListener( "enterFrame", bob ) function bob.collision(self,event) if( not self.isAlive ) then return false end if( event.phase == "began" ) then self.isAlive = false display.remove(self) end return false end bob:addEventListener("collision") function bob.finalize(self) Runtime:removeEventListener("enterFrame",self) -- GOTTA DO THIS! self:removeEventListener("collision") -- DO NOT NEED TO DO; AUTOMATIC self:removeEventListener("finalize") -- DO NOT NEED TO DO; AUTOMATIC end bob:addEventListener("finalize")

Thanks,  you have explained how stuff works which is great.  I now have things to ponder.