ID:1562427
 
Keywords: v
(See the best response by Ter13.)
Code:
tank
var/hp = 10

Click()
var/mob/m = usr
m.ShootAtTarget(src) // shoot at self

proc
TakeDamage(var/iDamage)
hp -= iDamage
if (hp <= 0)
Explode()

Explode()
del(src)

mob
var/obj/tank/target
proc
ShootAtTarget(var/obj/tank/t)
t.TakeDamage(10)


Problem description:
Lets say you have a tank(tank1) on the screen and when you click on other tanks, tank1 shoots missiles until the targeted tank is dead. In this case though, you select tank1 itself. Now tank1 shoots missiles at itself until it's dead. In the code above, I was expecting the tank to delete itself from existence but instead, tank1 remains "alive". What's a good way to handle this scenario?
Ah, think I found what I was looking for under garbage collection.

"... or even an object with a variable that points to itself. In rare cases, you may even depend on this behavior. When you are done with such objects, you should either null out the circular reference, or you should forcibly destroy each object with the del instruction. "

Still not sure how to null it out and I thought I was forcibly destroying it with del.
My advice would be to not use the "del" keyword at all, except in very rare, and very specific cases (HINT: this isn't one of them.)

To null an object out, you have to make sure that there are no variable references to the object.

Set its location to null, then set any variables that are keeping the object around to null.
Wouldn't this cause an insane amount of memory leaks?
Below is an example. Because o changes to null and loc is null (and it was never set anywhere else) it gets picked up by the garbage collection before the proc finishes, even though I never call del.

obj/Del()
world << "[src] gone!"
..()

mob/var/obj/o
mob/verb/collectGarbage()
o = new(src.loc)
sleep(10)
//we don't need it in loc anymore, so null it out:
o.loc = null
o = null
sleep(100)


It is implicitly removed, and that is what referring to the garbage collection is. Explicit calls to del are not garbage collection and come with some overhead due to a need to search references and remove them (such as it would need to look for the loc and the o, and set them to null before deleting them).

Others have made many posts around garbage collection recently, and I don't think I can go into depth as much as them, so I will leave my explanation there. :)
In response to GamerMania
GamerMania wrote:
Wouldn't this cause an insane amount of memory leaks?

No. Careless code causes memory leaks. Manual deletion will cause severe CPU problems, though.
Is using del(src) on a single obj really going to cause severe CPU problems? Is that based on deleting massive things or is deleting a few tanks from a game something I need worry about?
Best response
Well, the del keyword basically has to hunt through every variable of every object in the world to ensure that there are no references to the right-hand label (src, in your case).

Meaning, it's an iterative process that will take:

O(DV), where D is the number of datums in the world, and V is the number of variables defined in each datum.

As you can probably see, eventually this process hits critical mass, where you have so many objects in the world that searching through each and every one of them, and each and every one of their variables for references to the object you intend to delete can become quite cumbersome. (In my test environments, once you hit about 2 million objects, it can take upwards of 5 seconds to delete a single object manually using the del keyword. 2 million objects is not as much as it sounds.)

For the sake of best-practices, I'd recommend avoiding manual garbage collection entirely, and ensuring that the reference counter will always be reduced to zero for an object when it's no longer required. This is of course, done by simply writing code that is sane, clean, and sustainable.

I'm of the opinion (as is Tom), that the del keyword would probably be better deprecated than carried on. Of course, deprecating it would break pretty much every game on BYOND at the moment, and people would be forced to learn how to write code that doesn't leak constantly.
Not sure that you had to go that far with it but the explanation portion makes sense to me now and helps me understand why I would want to avoid forcing it.
I should mention the one case where manual deletion is a good thing:

temp_prompt
proc
_alert(Usr,Message,Title,Button1,Button2=null,Button3=null)
return alert(usr,Message,Title,Button1,Button2,Button3)


Using this object structure will allow you to remove the prompt from the user's screen by deleting the datum:

mob
var
temp_prompt/current_prompt
verb
do_something()
var/temp_prompt/tp = new()
if(usr.current_prompt)
del usr.current_prompt
usr.current_prompt = tp
. = tp._alert(usr,"This is a prompt.","Prompt","OK","Cancel")
if(tp)
if(.=="OK")
usr << "Pressed OK"
else
usr << "Pressed [.]"
Move()
if(current_prompt)
del current_prompt
. = ..()


The above example is about the only correct usage of del I can imagine. There may be more, but it's not likely.