Proper way to clear self variables in a OOP class?

so im trying to clear all my self variables in my class to clean up memory and optimize. i’m not really sure whether setting self to nil would clear all of its references in my clean up function `:Complete’.

i assume you have to clear all connections before removing all the references so i’ve done that just to make sure. i have also destroyed my gui buttons before clearing the table as well.

are you meant to set each self variable to nil on clean up like this?

	self.player = nil
	self.buttonTemplate = nil
	self.buttons = nil
	self.connections = nil
	self.progress = nil
	self.maxButtons = nil

or can u do it like this?

self = nil

thx so much!

code:

function ClickTask.new(player, buttonTemplate)
	local self = setmetatable({}, ClickTask)
	
	self.player = player
	self.buttonTemplate = buttonTemplate
	self.buttons = {}
	self.connections = {}
	self.progress = 0
	self.maxButtons = 5
	
	return self
end

function ClickTask:Complete()
	-- disconnecting any left over functions
	for _, conn in ipairs(self.connections) do
		if conn and conn.Connected then
			conn:Disconnect()
		end
	end
	
	-- delete the ClickTaskGui
	if self.ClickTaskGui then
		self.ClickTaskGui:Destroy()
	end
	
	self = nil
end

No, you cannot simply do self = nil. This will not properly clear the values; however, I believe setmetatable should do the trick like so:

setmetatable(self, nil)

Perhaps try testing this by printing self out after attempting to clear it.

1 Like

just use ECS bro.
ECS would let you clean up everything.
For now deleting metatable and in some cases table.clear() may work but as i said OOP is a joke commit some Entity Component System :fire:


function ClickTask:Complete()
	-- disconnecting any left over functions
	for _, conn in self.connections do
		if conn and conn.Connected then
			conn:Disconnect()
		end
	end

	-- delete the ClickTaskGui
	if self.ClickTaskGui then
		self.ClickTaskGui:Destroy()
	end

	setmetatable(self,nil)
	table.clear(self::{})
end

return function(player, buttonTemplate)
	local self = {}

	self.player = player
	self.buttonTemplate = buttonTemplate
	self.buttons = {}
	self.connections = {}
	self.progress = 0
	self.maxButtons = 5

	return setmetatable(self, ClickTask)
end

You don’t really need to set anything to nil inside the instantiated object. Just call destroy on anything it creates like associated instances or other classes, make sure connections are disconnected, and call it a day. Once all references to the object are lost, it will be automatically cleaned up by the garbage collector.

for example,

local function createAndDestroyMyObject()
	local myObject = MyObjectClass.new()
	myObject:destroy()
end

createAndDestroyMyObject()

In this function, the object is created, destroy is called, and then the reference to myObject goes out of scope and is lost forever, so it will get picked up by the garbage collector, assuming MyObjectClass’s destroy method correctly disconnects any active signal connections, destroys any instances it created, and calls destroy on any classes it created inside.

To further clarify, I do not need to set myObject = nil to “clean it up” in the same way that MyObjectClass.destroy does not need to set all the fields of the instantiated myObject to nil. The myObject table itself will be GC’d when there are no longer any references to it in memory.

3 Likes

What the hell did your LLM Chat GPT just feaver dreamed?
Where th you get method destroy? :skull_and_crossbones:
Why did you lobotomized your Chat GPT? :sob: :folded_hands:

This is a common misconception that I personally grappled with when I started using self.

What you did with ClickTask.Complete() is already good enough. You disconnected all events, and you also destroyed any instances you are not using anymore.

You do not need to set self = nil, as that’s redundant and doesn’t do anything. self is just a variable that has the value of the table you used the : (colon) operator on. Once you disconnect all events and there are no references to your metatable, the Lua gc will automatically clear the metatable from memory.

If that doesn’t make sense, you can think of it this way:

local function foo(thisIsAVariable: number)
    local variablePlusOne = thisIsAVariable + 1

    thisIsAVariable = nil --[[
        setting this variable to nil does absolutely nothing
        once this function completes its execution, "thisIsAVariable" will
        automatically be garbage collected, since there will be no more
        references to it
    ]]
end
2 Likes

The best way to understand this is to learn about references and garbage collection. Have a look here: A Beginner's Guide to Lua Garbage Collection

I recommend creating your own cleanup methods like you have done, but some people prefer to use modules like Maid by Quenty. I think those are fine to use once you understand how they work, I think it’s a bad idea to just use them without understanding how garbage collection actually works..

1 Like

extremely helpful information. everything makes a lot more sense now. thank you so much for taking the time to help me!

at least i know now that setting self to nil is pretty redundant. :zipper_mouth_face:

thanks so much for your help! after reading a lot more about the garbage collection that post was very helpful for my situation.

from what i gathered, the garbage collector doesn’t clean up connections or instances so that’s what i cleaned up in my clean up function. i’m not sure whether you should clean up tweens and coroutines (that’s if u can actually do so) but yeah!

huge thank you for your help mate, i really appreciate it!

While I didn’t show how MyObjectClass is set up, it is quite normal practice to include a :destroy() method in the same way as including a .new() method. It’s not a built-in, it’s just something I define on all my classes to have a standardized way of calling a deconstructor.

At the risk of over-clarifying, I’ll provide the following example. Imagine MyObjectClass has a deconstructor that calls :destroy() on some other class it created in its constructor:

local SomeOtherClass = require(path.to.SomeOtherClass)

local MyObjectClass = {}
MyObjectClass.__index = MyObjectClass

export type ClassType = typeof(setmetatable(
	{} :: {
		_someProp: SomeOtherClass.ClassType,
	},
	MyObjectClass
))

function MyObjectClass.new(someValue: string)
    local self = {
        _someProp = SomeOtherClass.new(someValue),
    }
    setmetatable(self, MyObjectClass)
    return self
end

function MyObjectClass.destroy(self: ClassType)
    self._someProp:destroy()
end

return MyObjectClass

Notice how it’s not necessary to set self._someProp = nil in the deconstructor. As long as I call MyObjectClass:destroy() and then lose the reference to the instance of MyObjectClass, its internal reference to an instance of SomeOtherClass will also be cleaned up by the garbage collector. Having some standardized way of chaining deconstructors such as with a :destroy() method is useful.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.