Setting an instance to nil from within the instance?

Hi all,

Question for advanced coders ahead:

I’m writing some clean-up code for my game and have some questions about how to remove instances of objects.

My code architecture is as follows: I have a level object. The level object has (as a child so to speak) an enemiesManager object. This enemiesManager objects has a table that contains all enemies in a scene. These enemies are instances of a pseudo-OOP object - I have an enemyClass module that is required by the enemiesManager object, and the enemy instances are table created and returned by a constructor method, called like this:

enemiesManager.enemyList[index] = enemyClass.new()

Now my big question is, how to nil this object, and more specifically, can you nil an object from within it’s own code.

At the moment I have a self.kill() function that removes all event listeners, cancels all timers and removes all display objects. The self.kill() function is called by the enemiesManager for every entry in the enemyList table.

So far so good. Then I have two options:

  1. I add “self = nil” to the self.kill() function after removing event listeners, timers and display objects.

  2. I call "enemiesManager.enemyList[index] = nil for every enemy in the list.

I would prefer method 1 because it means my objects works very “autonomously”. But this does feel tricky: can you call a function that is being deleted while it’s being run. Or in other words: can you delete a function (by nilling) while it’s being executed? Or is nothing being deleted, but just the pointer being removed by nilling, after which the code can be garbagecollected because there is longer a pointer to the code.

And are all pointers removed when you nil from within the object, because the enemiesManager.enemyList is then still a pointer, no? In this case, there would be another option:

  1. Calling nil both from within and from the enemiesManager to nil the list entries.

So, experienced Lua coders: which is best? And is it even “allowed” to nil and object from within its own code? I wrote some test code, but it’s very tricky to get concrete insights, because it’s very hard to tell if tiny tiny differences in memory use (and yes, I create 1000s of objects to scale up my results) are caused by my code or by the way garbagecollecting works.

Sheesh. I’m getting confused just writing about this!

  1. won’t do anything, “self” is a local variable to the function, it’ll be nil anyway when the function exits, but won’t release the reference _ still held _ by your manager, so enemy won’t actually be deleted.

  2. yes, this is the way you would do it.  this assumes that the enemy can “see” the manager, ie manager is in scope, typically done by giving each enemy a reference to its manager, fe:

    – instead of: enemiesManager.enemyList[index] = enemyClass.new() – do this instead: local enemy = enemyClass.new() enemiesManager:add(enemy) – in manager class: function EnemiesManager:add(enemy) enemy.manager = self – enemy can now use this ref to get its manager and call “manager:remove()” self.enemyList[#self.enemyList+1] = enemy end

  3. same as 2)

Hi,

if you write self=nil, you just made the self local reference to nil.

local function createEnnemy() local ennemy=display.newGroup() ; ennemyList[ennemy]=ennemy (...) attribute ennemy.isEnnemy=true (...) function ennemy.kill=function() ennemy:removeEvent(...) (...) ennemyList[ennemy]=nil ennemy=nil end return ennemy end

The function read it’s own code, then removed any reference to ennemy, and next leave the function. After that the ennemy object is unreachable so that the garbage collector can remove it.

This is turning out to be an interesting thread :slight_smile:

To both Dave and Yvan, some responses:

Dave, you go first :smiley: :

In my case, I don’t think the enemies need to “see” the enemiesManager, because the command to clear up the enemies is done on all enemies at once. When my enemies die individually, they just become “invisible” to the player, but remain in existence. Cleanup happens when the level ends (or the player quits), and then it’s then enemiesManager that talks to all it’s children in the enemiesList.

That being said, my child objects always know their parent because that’s just smart coding. I actually do things differently, like this:

self.enemiesList = enemyClass.new(self) – where self is the enemiesManager, and it passes a reference to itself to a newly created child object

Then in the enemyClass the constructor says:

self.new = function(super)

    local self = {}

    self.super = super

    return self

end

That way each child object knows his parent (or super) by the variable self.super

Most of the time, I also created a variable self.type = “enemiesManager” or something like that for each object, so that I have some help when I’m coding complex stuff --> that way I can print out self.super.super.super.super.type, to go up the hierarchical tree which happens more often than you think :slight_smile:

The other way around is easier, because downwards in the hierarchy, everything is named, so it’s more like:

appEngine.levelView.enemiesManager.enemyList[1].bulletManager.bulletList[1]

Okay then, Yvan, some response for you as well :stuck_out_tongue: :

What are the ( . . . ) things in your code??? I’m confused! Also, I’m not sure I agree with your comment. My code is, in general lines, the same as your code, only I name my object self instead of ennemy.

So at the start I say self = display.newGroup() instead of ennemy = display.newGroup(). And at the end I say self = nil instead of ennemy = nil. So your statement “if you write self=nil, you just made a local reference to nil, not the object itself.” doesn’t make sense to me.

Okay, you didn’t know that I called my object “self” of course. But leaving that aside, you seem to contradict what Dave states. In your code, the enemiesManager still keeps the reference, so the object won’t be cleaned up, no? Or at least, that is what Dave implies, as far as I understand.

Interesting stuff, right? :slight_smile:

By the way, I’m getting a headache just thinking of this! Maybe I’m just not going to clean up and let my app crash after a couple of hours :lol:

none of the “structural” details actually matter - what DOES matter, per your OP (title and body “how to nil this object”), is that you release the reference in the manager’s list (which will accomplish it), not the local reference “self” inside an enemy method (which will do nothing).

the “timing” of that is up to you (end of level, or whenever) as well as the “access path” (how you traverse class structure), the only issue of importance is releasing the “real” reference/s, wherever it/they is/are.  only then can garbage collection happen.

You are very right of course. Thanks for the clarification! I wasn’t seeing it, but with your help it becomes superclear:

The “real” reference to the object is held in the enemiesManager.enemiesList table, so this is where it needs to be nilled.

Thanks! I’m trying very hard to NOT leave all object removal stuff for the end of this project :wink:

  1. won’t do anything, “self” is a local variable to the function, it’ll be nil anyway when the function exits, but won’t release the reference _ still held _ by your manager, so enemy won’t actually be deleted.

  2. yes, this is the way you would do it.  this assumes that the enemy can “see” the manager, ie manager is in scope, typically done by giving each enemy a reference to its manager, fe:

    – instead of: enemiesManager.enemyList[index] = enemyClass.new() – do this instead: local enemy = enemyClass.new() enemiesManager:add(enemy) – in manager class: function EnemiesManager:add(enemy) enemy.manager = self – enemy can now use this ref to get its manager and call “manager:remove()” self.enemyList[#self.enemyList+1] = enemy end

  3. same as 2)

Hi,

if you write self=nil, you just made the self local reference to nil.

local function createEnnemy() local ennemy=display.newGroup() ; ennemyList[ennemy]=ennemy (...) attribute ennemy.isEnnemy=true (...) function ennemy.kill=function() ennemy:removeEvent(...) (...) ennemyList[ennemy]=nil ennemy=nil end return ennemy end

The function read it’s own code, then removed any reference to ennemy, and next leave the function. After that the ennemy object is unreachable so that the garbage collector can remove it.

This is turning out to be an interesting thread :slight_smile:

To both Dave and Yvan, some responses:

Dave, you go first :smiley: :

In my case, I don’t think the enemies need to “see” the enemiesManager, because the command to clear up the enemies is done on all enemies at once. When my enemies die individually, they just become “invisible” to the player, but remain in existence. Cleanup happens when the level ends (or the player quits), and then it’s then enemiesManager that talks to all it’s children in the enemiesList.

That being said, my child objects always know their parent because that’s just smart coding. I actually do things differently, like this:

self.enemiesList = enemyClass.new(self) – where self is the enemiesManager, and it passes a reference to itself to a newly created child object

Then in the enemyClass the constructor says:

self.new = function(super)

    local self = {}

    self.super = super

    return self

end

That way each child object knows his parent (or super) by the variable self.super

Most of the time, I also created a variable self.type = “enemiesManager” or something like that for each object, so that I have some help when I’m coding complex stuff --> that way I can print out self.super.super.super.super.type, to go up the hierarchical tree which happens more often than you think :slight_smile:

The other way around is easier, because downwards in the hierarchy, everything is named, so it’s more like:

appEngine.levelView.enemiesManager.enemyList[1].bulletManager.bulletList[1]

Okay then, Yvan, some response for you as well :stuck_out_tongue: :

What are the ( . . . ) things in your code??? I’m confused! Also, I’m not sure I agree with your comment. My code is, in general lines, the same as your code, only I name my object self instead of ennemy.

So at the start I say self = display.newGroup() instead of ennemy = display.newGroup(). And at the end I say self = nil instead of ennemy = nil. So your statement “if you write self=nil, you just made a local reference to nil, not the object itself.” doesn’t make sense to me.

Okay, you didn’t know that I called my object “self” of course. But leaving that aside, you seem to contradict what Dave states. In your code, the enemiesManager still keeps the reference, so the object won’t be cleaned up, no? Or at least, that is what Dave implies, as far as I understand.

Interesting stuff, right? :slight_smile:

By the way, I’m getting a headache just thinking of this! Maybe I’m just not going to clean up and let my app crash after a couple of hours :lol:

none of the “structural” details actually matter - what DOES matter, per your OP (title and body “how to nil this object”), is that you release the reference in the manager’s list (which will accomplish it), not the local reference “self” inside an enemy method (which will do nothing).

the “timing” of that is up to you (end of level, or whenever) as well as the “access path” (how you traverse class structure), the only issue of importance is releasing the “real” reference/s, wherever it/they is/are.  only then can garbage collection happen.

You are very right of course. Thanks for the clarification! I wasn’t seeing it, but with your help it becomes superclear:

The “real” reference to the object is held in the enemiesManager.enemiesList table, so this is where it needs to be nilled.

Thanks! I’m trying very hard to NOT leave all object removal stuff for the end of this project :wink: