How to use a Maid class on Roblox to manage state (2024)

James Onnen (Quenty)

·

Follow

Published in

Roblox Development

·

7 min read

·

Aug 28, 2019

--

The Maid class on Roblox is a useful class that I use to help manage state, especially in regards to signals or events. Be warned: This is a fairly technical article!

Maids are basically just syntactic sugar on handling connections and other clean up tasks in a way that allows you to store functions and connections in a container. It provides an interface for objects to clean themselves up properly.
~ GollyGreg, developer of QClash and a lot of other neat

Maids are really useful!

I use Maid extensively in Jailbreak. It vastly simplifies the cleaning up of events/objects, and gives me confidence that I will avoid memory leaks.
~ Badcc, developer of Jailbreak

Philosophical programming motivation: Why to use a Maid

You can skip this section if you want…

I use Maid extensively in Jailbreak. It vastly simplifies the cleaning up of events/objects, and gives me confidence that I will avoid memory leaks.
~ Badcc

One of the most common issues with the observable pattern is that an observable can keep an object alive. That is, because the signal creates a closure, and thus, a reference, to an object itself, the object will never be garbage collected. This is what Roact (or React in JavaScript), as well as Angular 2, and a variety of other frameworks are trying to solve. The common solution is to disconnect events upon object deconstruction.

Maids originally were designed to help deconstruct signals. A common pattern is that a signal connection will be created sometime later in an object’s lifecycle, but must be disconnected at the end of an object’s lifecycle if present. This results in objects structured like this:

In general, having this code resulted in a lot of cases where the connection or object being cleaned up wasn’t initially there, but could be in certain cases. In this case, we really want a way to hide this behavior. It’s especially messy in inheritance-like-situations where we need to clean up components in the base class, as well as the child class. In this case, the child class almost always has to call ParentClass.Destroy(self) , before it can do its own deconstruction logic.

You even see this issue in objects in C# or JavaScript. You see it especially in IDisposable C# objects.

The other issue with Dispose() or Destroy() being used at the end of an object’s implementation is it creates multiple places where you must remember a member instance exists, and this must be stored.

Additionally, there’s strong motivation to coupling clean-up routines with the direct site where an object is created. For example, when we create a tooltip, it is useful to know exactly when or where an object is being cleaned up. This really helps make it easy to track what is being cleaned up.

For this reason, we introduce the Maid object.

What is a maid, and why should I care?

A maid is an abstraction in charge of cleaning up a state. Maids can be given a task to clean up, which will then, at a later time, clean up that task. Maids can also be given a named task to clean up. If this task is overwritten, it will clean that task up on the spot.

In short, Maids help manage state in your object. Maids can help augment an existing object in your game, encapsulating the deconstruction logic, or can help maintain closures in a cleaner way.

How to use a maid: An overview of an API

First you will need to import the Maid library. This can be done right now by copying this code into a module script, and then requiring the module script.

You can download the Maid source code here:

You construct a new maid by calling:

local maid = Maid.new()

In a class, I like to construct a maid at the beginning as a private member, and then have the deconstructor call the Maid’s destroy method:

function MyClass.new()
local self = setmetatable({}, MyClass)
self._maid = Maid.new() return self
end
function MyClass:Destroy()
self._maid:Destroy()
end

Once a maid is created, there are several ways to assign a task to a maid for cleanup. The first way is to simple call the method :GiveTask() . Alternatively, the Maid can also be overwritten with a key that is not reserved in a maid. For example, you can do

local maid = Maid.new()
maid.cleanBaseplateTask = workspace.Baseplate

In this case, the maid is now planning to clean up the baseplate when it’s told. The base can take the following types of arguments that can be cleaned up.

  • function: The function will be executed upon cleanup
  • event connection: The event connection will be disconnected upon cleanup
  • maid: The maid’s cleanup function will be invoked upon cleanup
  • Roblox object: The Roblox object will be destroyed upon cleanup
  • nil: Only forces cleanup

Cleanup occurs in two ways. The first way is on overwrite a key. For example, in this case, the Baseplate will be immediately destroyed:

local maid = Maid.new()
maid.cleanBaseplateTask = workspace.Baseplate
maid.cleanBaseplateTask = nil

Additionally, this would also force a cleanup of the baseplate while also adding a new baseplate to track to clean up.

local maid = Maid.new()
maid.cleanBaseplateTask = workspace.Baseplate
local newBaseplate = workspace.Baseplate:Clone()
newBaseplate.Parent = workspace
maid.cleanBaseplateTask = newBaseplat

Secondarily, maids can be cleaned up by invoking the :DoCleaning() method, which is the same as a Maid’s :Destroy() method. Maids can be asked to clean multiple times. This looks like this:

local maid = Maid.new()
maid.cleanBaseplateTask = workspace.Baseplate
maid:DoCleaning()

In this case, a maid will clean up all of its tasks.

Note that :GiveTask() actually is also generating a tracking key for that task. For example, you can use :GiveTask() to get a new unique ID for a task that can later be cleaned up. For example

local maid = Maid.new()
local cleanupBaseplateTaskId = maid:GiveTask(workspace.Baseplate)
maid[cleanupBaseplateTaskId] = nil

will actually clean up the baseplate.

Note that maids accept other types of arguments for cleaning besides Roblox parts. The most used one is connections, which generally must be cleaned up when an object is being used. For example, you might write this:

local function onTouchStarted(inputObject)
local maid = Maid.new()
remoteEvent:FireServer("TouchStarted") maid:GiveTask(function()
remoteEvent:FireServer("TouchStopped")
end)
maid:GiveTask(UserInputService.InputEnded:connect(
function(_inputObject)
if _inputObject == inputObject then
print("Touch ended")
maid:Destroy()
end
end))
end

In this case, you’re easily able to capture cleaning up both the touch started event, and also the event listening for an input end. In this case, maids are able to create a lot of extra value here in something that might otherwise be painful to write.

Case study: Animating with RenderStepped and Maids

It may be hard to see why maids are inherently useful. One example of when I like to use maids is when I bind to RunService.RenderStepped in Roblox. I like to have a method setup like this:

local function startUpdate()
if maid._updateConnection and maid._updateConnection.Connected then
return -- already animating
end
maid.updateConnection = RunService.RenderStepped:Connect(function()
if animationIsDone() then
maid.updateConnection = nil
end
doAnimationUpdate()
end)endlocal function setAnimationTarget(target)
-- set target here

startUpdate() -- you can call this freely since maid is tracking state
end

This sort of pattern means that an animation can be stopped because it’s done, thus, stopping the render stepped update, or it can be stopped because the maid is cleaned up, common when you’ve associated an animation with a maid.

Implementation details: Maid cleaning order gotchas

One specific detail about the Maid implementation I linked is that it’s intentionally designed to work such that a maid in cleaning can still add more tasks to be cleaned, and they will be cleaned. This means you can give a maid a task that will clean other tasks before (such as disconnecting signals) additional tasks get executed. Additionally, maids disconnect all events before executing other cleaning components to make sure that maids don’t invoke themselves into an infinite loop.

If a maid’s cleaning task errors (for example, a function), then other tasks may not clean. This is designed to help maids execute permanently, and also report stack traces properly. However, beaware that maids are not guaranteed to execute in an isolated environment, so don’t use wait() or other async operations in a maid, such as invoking a RemoteFunction in a deconstructor.

Maids and promises

Maids are also designed to take in promises. Giving a maid a promise will result in a new promise that will be rejected in the event of the maid being cleaned up. This is especially useful to help manage state whereas promises can be invoked after the lifetime of an object. For example, in another case you might find yourself checking if self.Destroy is defined before letting a promise continue. Giving a promise to a maid is quite valuable in this regard. This API is specifically for promises, and looks like this:

local maid = Maid.new()
local promise = ... -- some promise
maid:GivePromise(promise):Then(...) -- do operation here

In this case, promises and maids can be very valuable together.

Summary

Overall, maids are a really useful tool that can be used to handle events. There are hundreds of points where maids are used across my code.

How to use a Maid class on Roblox to manage state (3)

I know other top developers are also using maids in their code to help manage state. I highly recommend looking into Maids as a tool to clean up your code further.

self._tooltip = tooltip
self._connection = tooltip.Hidden:Connect(function()
self._connection:Disconnect()
self._connection = nil
self._tooltip:Destroy()
self._tooltip = nil
end)
end
function MyClass:Destroy()
if self._tooltip then
self._connection:Disconnect()
self._connection = nil
self._tooltip:Destroy()
self._tooltip = nil
end
end
return MyClass
How to use a Maid class on Roblox to manage state (2024)

FAQs

What is the maid function in Roblox? ›

Maids are effective at managing RBXScriptConnections that often are causes of memory leaks in scripts, often due to connections not being disconnected in time or a reference still leading to the said connections.

How do you edit your place in Roblox? ›

In the left-hand navigation, under Configure, select Places. Click the Edit in Studio button for the intended new start place. Studio opens and loads that specific place.

What does get children do in Roblox? ›

GetChildren gets everything inside the place where you want. GetPlayers only gets the Players. There are only player objects in game. Players so GetChildren will also get all the players.

How do you make a giver on Roblox? ›

How to Make a Click to Receive Giver in Roblox
  1. Step 1: If You Want to Do It the Easy Way. place my model in your game and follow the instructions in the script called "READ ME". ...
  2. Step 2: Open Studio and Set Vew. ...
  3. Step 3: Make the Giver. ...
  4. Step 4: Scripting. ...
  5. Step 5: All Done!

How to get Robux for free? ›

Question: Can I earn Free Robux? Answer: No. Robux are purchased from the Roblox company for real world currency or earned as a Robux creator.

How do you edit your body in Roblox? ›

Log into your Roblox account on the Mobile Application. Go to the Avatar tab. Click on "Customize" and select "Body".

What language is Roblox written in? ›

Roblox uses the coding language Lua. This article will cover how to start coding in Roblox, introducing common concepts like scripts, data types, and variables. By the end, you'll be able to type out code that displays messages in Roblox Studio.

What does LoadCharacter do in roblox? ›

The LoadCharacter Player function creates a new character for the player, removing the old one. It also clears the player's Backpack and PlayerGui.

What does parent do in Roblox Studio? ›

The Parent property determines the hierarchical parent of the Instance. The following terminology is commonly used when talking about how this property is set: An object is a child (parented to) another object when its Parent is set to that object.

References

Top Articles
Latest Posts
Article information

Author: Velia Krajcik

Last Updated:

Views: 6148

Rating: 4.3 / 5 (74 voted)

Reviews: 81% of readers found this page helpful

Author information

Name: Velia Krajcik

Birthday: 1996-07-27

Address: 520 Balistreri Mount, South Armand, OR 60528

Phone: +466880739437

Job: Future Retail Associate

Hobby: Polo, Scouting, Worldbuilding, Cosplaying, Photography, Rowing, Nordic skating

Introduction: My name is Velia Krajcik, I am a handsome, clean, lucky, gleaming, magnificent, proud, glorious person who loves writing and wants to share my knowledge and understanding with you.