Hi folks,
I’ve done tons of research and testing on the proper way to write a module in Lua. So I thought I’d share it with you.
Most people would simply write a module like a regular script starting with
[lua]module(…, package.seeall)[/lua]
Have a look here to get some ideas about why it is a bad practice.
What you should write, according to many gurus is this:
[lua]-- File: myModule.lua
– Author: me
– ***************************
– Private part of your module
– ***************************
local myOtherModule = require( “myOtherModule” )
local privateVar1 = “Foo”
local privateVar2 = 23
local privateFunction1()
– do something
end
local privateFunction2()
– do something else
end
– The module table
local myModule = {}
– **************************
– Public part of your module
– **************************
myModule.publicVar1 = 5
myModule.publicFunction1 = function()
myModule.publicVar1 = 22
– do something
end
myModule.publicFunction2 = function()
– do something else
end
return myModule[/lua]
A big advantage of this technique is that it avoids cluttering the global environment with unnecessary stuff. And even more important, it allows you to write private functions and variables that won’t be accessible outside the module.
To call and use the module, you would just write:
[lua]local myModule = require(“myModule”)
myModule.publicFunction1()[/lua]
Now, let’s talk a bit about memory. As you are aware, you should call require in every script that makes use of the module. Worry not, the require function actually loads the module only once.
What it does is that it stores the result in a special table called “package.loaded”. So if you load “myModule”, you will see an entry as package.loaded.myModule (or package.loaded[“myModule”]).
When the module gets loaded the first time, the return value is stored as the value associated with the key “myModule”.
For a simple script with no return value, it’s actually “true”. Why? Because if you don’t specify a return value, it is assumed to be “nil”. And setting the table entry to “nil” would mark it as ready to be deleted by the GC. And this can’t happen as “require” needs to know which module is loaded. So what’s in this entry when you create a module? A reference to your table of course. So this is what happens when you call require multiple times:
First time:
-
The script is executed. An entry is created in “package.loaded” and the value is set to the return value of your script. In case of a module, it’s gonna be a table.
-
the local variable is assigned the reference to the table
Second time:
-
“require” checks in “package.loaded” and sees that it’s already available. nothing is done.
-
the local variable is assigned the reference to the table
As you can see, no time and memory is wasted in multiple calls to “require”. BUT. And this is a big BUT (no, not a big butt …):
The memory used by the module is never freed!
What the heck? Yep, when the script requiring a module finishes its task, the local variable referencing the table is GCed. But there is still a reference to this table in “package.loaded” so the table never gets freed. Bugger.
You can check it out by yourself:
[lua]main.lua:
– load a module
local module = require(“module”)
– quit main and execute the script
require(“script”)
module.lua:
local myModule = {}
myModule.dummyVar = 1
return myModule
script.lua:
for k,v in pairs(package.loaded) do
print(k,v)
end[/lua]
So how do you get rid of the loaded module? Easy:
[lua]package.loaded[“myModule”] = nil
collectgarbage(“collect”)[/lua]
And with this, I’m going back to work. I apologize in advance for any typos / english error as I’m french.
Seth [import]uid: 51516 topic_id: 9114 reply_id: 309114[/import]