Custom Changed event for variables

The title speaks for itself. This module will allow you to listen for any changes made to a variable. It’s supposed to be similar to the .Changed event used for Instances and whatnot.

  • You can get the module here.
  • The module does not support tables.
  • The module itself uses OOP, or object oriented programming.

This script is included in the module along with an additional one.

-- This script will show you how to use the module


local changedEventModule = require(script.Parent) -- Require the module
local exampleVariable = "foo bar" -- This is the variable we will want to be listening for changes.


-- We must declare a new variable for the changed event to work, and then continue off said variable. Pass exampleVariable as the argument.
local listeningVariable = changedEventModule.new(exampleVariable)


-- Now we must listen for changes via Listen() method using the new variable we declared.
listeningVariable:Listen()


-- Set up listener
local listener = listeningVariable:GetListener()
listener.Event:Connect(function(value)
	print("Variable has changed! New value: "..value)
	-- do stuff
end)

-- Alternatively, you can do these
-- listeningVariable:GetListener().Event:Connect(function(value)
-- listeningVariable.event.Event:Connect(function(value)


-- To change a value, you use the SetValue() method
listeningVariable:SetValue(123) --> Variable has changed!
-- listeningVariable.value = 123 --> Variable has changed!


-- To get a value, you use the GetValue() method
local var = listeningVariable:GetValue() --> 123
--local var = listeningVariable.value --> 123


-- To stop listenting for changes, you use the StopListening() method
listeningVariable:StopListening() -- The listener handler will not function until you listen for changes again.


listeningVariable:SetValue("foo bar") --> No output, because it's currently not listening for changes. The value will still change.
-- listeningVariable.value = "foo bar" --> No output ^^^^


-- Now we can listen for changes again
listeningVariable:Listen()


listeningVariable:SetValue(1234) --> Variable has changed!

If you have any suggestions or questions for me, let me know.

2 Likes

bindable events are different then Variable.Changed
though there are no use cases where this module would need to be used

there are other things you can do to achieve the same thing without a changed event

this module uses bindable events

1 Like

This little library is kind of useless for the most part, but if you need it, there’s some problems with it.

The main problem is that it polls.
Which means it’s constantly asking: “Is this value changed?”
And that’s not optimal, it’s horrible for performance, especially if you have a bunch of these around.

You can just instead, :Fire the bindable whenever the value changes, not having this problem at all.

This does it like that:

local Node = {}
Node.__index = Node

function Node.new(variable)
    local self = setmetatable({
        _value = variable,
        _bindable = Instance.new("BindableEvent")
    }, Node)
	
    self.Changed = self._bindable.Event
	
    return self
end


function Node:GetValue()
	return self._value
end


function Node:SetValue(newValue)
	if self._value == newValue then
		return 
	end
	
	self._value = newValue
	self._bindable:Fire()
end

function Node:Destroy()
	if not self._bindable then
		return
	end
	
	self._value = nil
	self.Changed = nil
	self._bindable:Destroy()
    self._bindable = nil
end


return Node
6 Likes

nice, only the Destroy function isnt really needed. if you make the variable in script nil the binable event will be removed automaticly since its parent is nill and its not stored by script anymore either.

This isn’t actually completely true, logically it would be, but when you mess with instances the entire thing gets messed up and garbage collection is way more of a crybaby.

There is info about this here.

Usually self-referencing references would not cause an issue to the garbage collector, however when it comes to using a BindableEvent this is a lot more problematic.
Basically the layer between Lua and C (Instances and by extension BindableEvents and RBXScriptSignals) doesn’t understand references that well, and basically if you ever reference anything that is or has a reference to that BindableEvent, it will count as a strong reference even if the only reference is a reference inside that Instance’s event connection which would usually not matter since its disconnected from the rest of the game.

Basically, referencing your bindableevent inside that bindablevent’s connection completely breaks garbage collection for that instance, and so if you want to clean that up you need to destroy the bindableevent or disconnect everything before hand in some way.

What this means is that if you reference the Node in a connection on .Changed on it, that will be a memory leak unless you destroy the Node before getting rid of its references.

The other better way is to not use a BindableEvent or any instance in general, and instead use a Lua implementation of a Signal. That way you don’t depend on weird Instance shenanigans like these.

1 Like