I have a metatable (named Item
) and I can't seem to find anything on google for this, but I want to add a function that will let me delete the item. I've looked at plenty of search results, and I either didn't understand them very well or it didn't work for me.
Here's my code:
local Item = {} Item.__index = Item function Item.new(ID, Amount) local NewItem = {} setmetatable(NewItem, Item) NewItem.ID = ID NewItem.Amount = Amount return NewItem end function Item:Delete() --not sure what to put here exactly --I've tried removing all of self's keys but that didn't do anything end local Apple = Item.new("apple", 1) print(Apple) --a table Apple:Delete() print(Apple) --still a table
I found that removing all references to a table effectively deletes it (or at least the garbage collector will delete it). I would do Apple = nil
to remove the reference (I think?) but 1. I need to know for sure that the table is gone, and it's not gonna sit there taking up memory forever, and 2. I can't do that inside the :Delete()
function. (self = nil
does nothing)
You are correct, when you remove all references to a table, it is deleted. This is taken care of by Lua's garbage collection:
However, there are reasons you might want some sort of cleanup function:
For all other cases, you can expect that it will be removed from memory as needed. (In regular Lua you can force garbage collection to occur immediately with the debug library's collectgarbage
, but Roblox has removed this functionality.)
For example, say these items are part of a player's inventory, and you have a dictionary tracking each player's inventory like playerInventory[player] = inventoryList
. So long as you perform playerInventory[player] = nil
when the player leaves the server, you don't need to do anything special to each inventory item, so long as the items don't do any of the above points.
If you wish to ensure that a piece of code isn't going to leak memory, you can use tables with weak keys/values to test, such as these functions I've written for that purpose:
local function waitForGarbageCollection() -- Creates garbage and waits for Lua to clean it up local t = setmetatable({}, {__mode="v"}) t[1] = {} while t[1] do wait() end end local function LeaksMemory(create, cleanup) -- create:function() must return 1+ values. -- cleanup:function(...) -- optional; given what create returned -- Prints status message (with indices of leaked values if needed). local t = setmetatable({}, {__mode = "v"}) local numValues do -- put create's values in temporary scope so they can be garbage collected local newValues = {create()} numValues = #newValues if numValues == 0 then error("create returned 0 values") end for i = 1, #newValues do t[i] = newValues[i] end if cleanup then cleanup(unpack(newValues)) end end waitForGarbageCollection() local numLeaked = 0 local report = ": " for i = 1, numValues do if t[i] ~= nil then numLeaked = numLeaked + 1 if numLeaked == 1 then report = report .. i else report = report .. ", " .. i end end end print(numValues > 1 and string.format("%d/%d leaked%s", numLeaked, numValues, numLeaked > 0 and report or "") or numLeaked == 1 and "leaked" or "no leak") end -- Example usage: local Item = {} Item.__index = Item function Item.new(ID, Amount) local NewItem = {} setmetatable(NewItem, Item) NewItem.ID = ID or 1 NewItem.Amount = Amount or 0 NewItem:Connect() return NewItem end function Item:Inc() self.Amount = self.Amount + 1 end function Item:Connect() self.con = game:GetService("RunService").Heartbeat:Connect(function() self:Inc() end) end function Item:Destroy() if self.con then self.con:Disconnect() end end LeaksMemory(Item.new) -- leaked LeaksMemory(Item.new, Item.Destroy) -- no leak -- Note: LeaksMemory isn't a perfect indicator, as it assumes you'll give it a reference to everything that might be left in memory. For instance, if we change the Connect function to not refer to self...: local a = 0 function Item:Connect() self.con = game:GetService("RunService").Heartbeat:Connect(function() a = a + 1 end) end LeaksMemory(Item.new) -- no leak -- ... you can see that it claims there is no leak when there really is one. If you can return a reference to everything that might be left in memory (from the function you pass in to LeaksMemory), you can get the full story: function Item:Connect() local function func() a = a + 1 end self.con = game:GetService("RunService").Heartbeat:Connect(func) return func end function Item.new(ID, Amount) local NewItem = {} setmetatable(NewItem, Item) NewItem.ID = ID or 1 NewItem.Amount = Amount or 0 return NewItem, NewItem:Connect() end LeaksMemory(Item.new) -- 1/2 leaked: 2 LeaksMemory(Item.new, Item.Destroy) -- 0/2 leaked -- The "1/2 leaked: 2" means that 1 of 2 items were leaked (and it was the item in the 2nd index that remained in memory), but calling Destroy ensured that even the function was removed from memory