Skip to content

Instantly share code, notes, and snippets.

@Corecii
Last active August 30, 2020 18:18
Show Gist options
  • Save Corecii/4099b26127f0bbb3431dd9853ec100ff to your computer and use it in GitHub Desktop.
Save Corecii/4099b26127f0bbb3431dd9853ec100ff to your computer and use it in GitHub Desktop.
Roblox Instance Destroyed and Garbage Collected Example Code
--[[
Notable behavior:
* ObjectValues will get their Value set to nil if:
* The ObjectValue is in nil
* its Value is in nil
* its Value gets garbage collected because it has no string reference
* When an object gets garbage collected, all of its connections get disconnected.
* Connections that don't hold a reference to the object won't prevent garbage collection!!
* When an object gets destroyed, connections are disconnected by the next Heartbeat
We can make use of these behaviors to detect when an object is both destroyed and garbage collected
]]
local OnDestroyed = {}
function OnDestroyed.onInstanceGarbageCollectedSimple(instance, callback)
local weakReference = Instance.new("ObjectValue")
weakReference.Value = instance
instance = nil
coroutine.wrap(function()
while weakReference.Value ~= nil do
wait(5)
end
callback()
end)()
end
-- The following will not detect if an object is already in nil and gets Destroyed:
function OnDestroyed.onInstanceDestroyedSimple(instance, callback)
local connection
connection = instance.AncestryChanged:Connect(function()
game:GetService("RunService").Heartbeat:Wait()
if not connection.Connected then
callback()
end
end)
instance = nil
end
function OnDestroyed.onInstanceDestroyedOrGarbageCollected(instance, callback)
local weakReference = Instance.new("ObjectValue")
weakReference.Value = instance
instance = nil
local changeIndex = 0
local connection
local function onChanged()
changeIndex = changeIndex + 1
-- Detects Destroyed and Garbage Collected:
game:GetService("RunService").Heartbeat:Wait()
if not connection.Connected then
callback()
return
end
-- Detects Garbage Collected:
if not weakReference.Value:IsDescendantOf(game) then
local thisChangeIndex = changeIndex
while connection.Connected do
wait(5)
if changeIndex ~= thisChangeIndex then
return
end
end
callback()
end
end
connection = weakReference.Value.AncestryChanged:Connect(onChanged)
-- If the object is already in nil, we need to start running the GC detector:
coroutine.wrap(onChanged)()
end
return OnDestroyed
local OnDestroyed = require(game.ReplicatedStorage.OnDestroyed)
local function testCase(name, setup)
local f = Instance.new("Folder")
setup(f)
OnDestroyed.onInstanceDestroyedOrGarbageCollected(f, function()
print("Test case \"" .. name .. "\" destroyed or garbage collected")
end)
f = nil
end
testCase("nil", function() end)
testCase("nil -> destroy", function(f)
delay(2, function()
f:Destroy()
end)
end)
testCase("workspace -> destroy", function(f)
f.Parent = workspace
delay(2, function()
f:Destroy()
end)
end)
testCase("workspace -> nil", function(f)
f.Parent = workspace
delay(2, function()
f.Parent = nil
end)
end)
testCase("workspace -> nil -> workspace -> nil", function(f)
f.Parent = workspace
delay(2, function()
f.Parent = nil
wait(2)
f.Parent = workspace
wait(2)
f.Parent = nil
end)
end)
testCase("workspace -> nil -> workspace -> destroyed", function(f)
f.Parent = workspace
delay(2, function()
f.Parent = nil
wait(2)
f.Parent = workspace
wait(2)
f:Destroy()
end)
end)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment