打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
A Better Approach to External Modules
Posted on by

But before I begin, here’s why I’m writing this article in addition to the previous modular coding tutorial that was posted just last month. In a comment to the previously published tutorial, Corona developer Steven K wrote:

modules are awesome especially when you work with several people on one project but I’m always afraid of using it because you have to use global functions and variables when you use modules

Don Quach (from Spriteloq) also chimed in:

From my experience, all modules create globals even if you try to specify local.

He recommended a third-party function, unrequire (below), which allows you to unload a module that you required-in:

1 2 3 4 5 6 7 8 9 10
function unrequire(m)
package.loaded[m] = nil
_G[m] = nil
end
 
-- USAGE:
--[[
require "modulename"
unrequire( "modulename" )
--]]
view raw unrequire.lua hosted with ? by GitHub

If you’re using external modules in the same way I described in my modular coding tutorial last month, then I highly recommend you make use of the unrequire() function (thanks Don!).

I was able to use the function in many instances (and it proved very useful!), but I ran into the difficulty of deciding when to use it for some modules.

What if you have a module that you’re regularly accessing functions from? After spending some time with it, I realized that the unrequire() function, while useful, tends to add more complexity to a project—more complexity than a Corona app should require (no pun intended). You should just be able to require things in without thinking about when to UN-require them.

My proposed solution? Since the general rule of thumb in Lua is to try your best to avoid using globals altogether, let’s see if we can do that in regards to external modules as well. Let’s restructure our modules so that we never cause them to force globals upon your otherwise pristinely coded app.

Abolishing the module() Function

Right now, you are likely creating an external module like so:

1 2 3 4 5 6 7 8 9 10 11 12 13
module(..., package.seeall)
 
function testFunction1()
print( "Test 1" )
end
 
function testFunction2()
print( "Test 2" )
end
 
function testFunction3()
print( "Test 3" )
end
view raw examplemodule.lua hosted with ? by GitHub

And here’s how you use the functions within that module:

1 2 3 4 5
local examplemod = require "examplemodule"
 
examplemod.testFunction1() -- prints "Test 1" to terminal
examplemod.testFunction2() -- prints "Test 2" to terminal
examplemod.testFunction3() -- prints "Test 3" to terminal
view raw testmodule2.lua hosted with ? by GitHub

The problem with examplemodule.lua (above) is that the module() function makes use of the global namespace (as you can see by looking at the unrequire() function). Not only that, the functions you define within the module are also defined as global functions, which is also easy to see (source).

Here’s a way you can rewrite examplemodule.lua (above) and still use it in exactly the same way, without needing functions like unrequire(), and without using globals—all while reducing the potential for memory leaks, errors, and crashes to occur.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
-- define a local table to store all references to functions/variables
local M = {}
 
-- functions are now local:
local testFunction1 = function()
print( "Test 1" )
end
-- assign a reference to the above local function
M.testFunction1 = testFunction1
 
local testFunction2 = function()
print( "Test 2" )
end
M.testFunction2 = testFunction2
 
local testFunction3 = function()
print( "Test 3" )
end
M.testFunction3 = testFunction3
 
-- Finally, return the table to be used locally elsewhere
return M
view raw examplemodule.lua hosted with ? by GitHub

I’ve inserted comments throughout the code to help explain it a little better, but the “gist” is that I’ve removed the use of the module() function, was able to localize everything within the module (no use of globals), and module usage remains completely identical, meaning that if you rewrite your modules using the method shown above, the rest of your code should be able to be exactly the same.

The use of Lua’s own module() function may very well be the reason why amazing scripts such as the Director Class (by Ricardo Rauber) and possibly some others end up causing memory usage to gradually climb, even when you’re absolutely sure you’re doing everything else right.

My challenge to you: rewrite all of your external modules in the format I described in my example above, to include your favorite scripts (such as Director). In short, I want you to abolish the use of the module() function, while still reaping the benefits of modular coding!


Posted by . Thanks for reading...

70 Responses to “A Better Approach to External Modules”

    • Jonathan Beebe

      @Neils: Thanks for the links! I didn’t know that module() has been deprecated (great to know though, thanks again!). Looks like this tutorial came with good timing

      Reply
  1. Tommy Gr?vnes

    Nice one

    will try it in my project. I wish this was part of the Lua language, if you replace local with private/public keywords, any public functions automatically goes in the M table, the private functions does not. Just a thought. Thx

    Reply
  2. Jon

    I actually use Ricardo’s loader class to do all of my module loading – everything then get’s wrapped up into a single object – however I shall look at changing the loader class and my functions that it loads into this new “module-less” format.

    Very informative post thanks Jon (and Niels for the extra info).

    Reply
  3. Jon

    Can anybody confirm if there is a difference between

    local testfunction = function(arg)
    ….
    end

    and

    local function testfunction(arg)
    ….
    end

    And what the difference is – which is more efficient?

    Cheers

    Reply
  4. Caleb

    Anyone feel like taking on the task of modifying director to not use the module() function? Richardo perhaps?

    Reply
  5. XenonBL

    Hi Jonathan,

    I tried to implement this in my own app but ran into a limitation of this approach, at least for the way I modularized.

    I require all my external modules in main.lua like so:
    local examplemod = require “examplemodule”

    The issue is that some functions of particular modules depend on functions from other modules. Using your method those functions are now invisible/undefined to other modules unless I remove ‘local’ from the require statement. I presume that puts me back to where I don’t want to be, with every function global.

    Is there a way where I can include external modules with dependencies on other external modules and still keep everything global?

    Reply
  6. David

    This explains why I thought I was going crazy… I kept thinking: “Why is my memory slightly increasing, but then hits a max???”

    Nice article!

    Reply
  7. Jonathan Beebe

    @XenonBL: Since the modules you are requiring in are completely local using this method, why not just require the modules in every module you need them? That is, unless you have some variables you need to access globally across the board (in which case you might just store those directly in the _G table if you cannot find a way around those… For functions though, I recommend just localizing the modules in every module you need them–that way you don’t have to make everything global in your main.lua.

    @Jon: Regarding function syntax, I have read *somewhere* that defining functions (local func1 = function()) yields better performance (but cannot call itself), but I can’t find where I read that. So for the most part, it’s just a matter of personal preference (unless some proper testing can be done, or some links to someone who has can be found).

    Reply
  8. David McCuskey

    Good writeup, Jonathan. I think this is a good way to keep things simple and modular – it is the method that I have used in my just-released Corona Library – http://developer.anscamobile.com/code/dmc-corona-library. I opted against the use of module() because I read read the same info in the links that @Niels provided (its use seemed more harmful than useful).

    @XenonBL: If you think some examples would help, I have created several in my library which use this method. In short, they will show what Jonathan suggested – require other modules in those which need them. If interested, they are in the library’s code base, located on github. Check out the link above for more information.

    Reply
  9. XenonBL

    @jonathan Yeah, that works, although at this point my code is separated into dozens of external modules/libraries, which would require a pretty extensive rewrite to get everything working using this new system. Plus, I’m also using third party modules such as Particle Candy, Text Candy, CrawlspaceLib etc. which all still rely on module().

    And what about Ansca’s own ui.lua? (I’m still using it and it’s found in many of the Ansca supplied sample projects ). Plus all the user submitted modules…

    Reply
  10. MarkRosewood

    I’ve used something like this and it seems to work fine. A couple of differences in the way I use it though…

    I go straight for:

    M.testFunction2 = function()
    print( “Test 2″ )
    end

    return M

    (or in my case function M.testFunction2() )

    i.e. don’t create a local function and then a global reference to it. Is there a difference? Aren’t you ‘globalising’ the function anyway by creating the reference? Does the two step version buy you anything?

    Other thing I do is have truly local functions inside the module which can only be called from within the module (like private methods) i.e.

    in Man.lua:

    local Man = {}

    – local private method
    local function getEyeColour(args)
    if(args.hairColour == “blonde”) then
    return “blue”
    else
    return “brown”
    end
    end

    – global public method
    function Man.getEyes(args)
    return getEyeColour({hairColour=args.hair})
    end

    – all done
    return Man

    and in main…

    local mark = require(“Man”)
    print(“eyes=” .. mark.getEyes({hair=”brown”}))

    Reply
  11. Jonathan Beebe

    @Mark: There’s really no difference, the reason why I did the two-step is because it’s easier to adapt existing modules, because if you were to add a table prefix to your functions, it could break other code that uses those functions. Doing it in the way I described above, it shouldn’t break any of your existing code within that module because the original functions still exist (you just need to localize them, is really the only thing you have to do with the functions).

    About being global, in your example, when you do:
    local Man = {}
    function Man.getEyes(args) … end

    Man.getEyes is NOT global because getEyes is a key to the table Man, which was localized above. It’s the same concept as forward declarations.

    The two things below are exactly the same, both utilize only locals, so it’s just a matter of personal preference:

    ex. 1:

    local M = {}

    function M.testFunction1() print( “Test 1″ ); end

    is the same as…

    ex. 2:

    local M = {}

    local function testFunction1() print( “Test 1″ ); end
    M.testFunction1 = testFunction1

    ——

    And to answer your question, making a reference to something doesn’t create a global, unless it’s a global you are storing it into. In the examples above, the tables that hold the references are also local, so there’s no difference when everything is returned at the end of the module.

    Reply
  12. MarkRosewood

    @jonathan: I get what you say about function Man.getEyes being a key in a local table (Man). Thanks for explaining that. I think I’ve confused the function being callable from outside the module with it being global. It is callable (where function getEyeColour isn’t) but they both die when you kill mark (gulp!) as they both belong to the Man table.

    Reply
  13. John P. Eurich

    @Jonathan: I’m very glad to see your post about eliminating the use of “module”, since it is depreciated in Lua 5.2.0. I’ve been using a technique very similar to yours for a couple of months so that my pure Lua code will run in both Lua 5.1.4 and 5.2.0. Once in a while I use iLuaBox on my iPad to develop and test out small pieces of Lua code, and iLuaBox is based on Lua 5.2.0. Thanks, for your post.

    Reply
  14. Nicholas Golden

    lucky for me I am a total n00b and don’t use modules. I am not using director (I know I should) and just wrote my own menu screen/option screen etc and clean things up when done.

    I really don’t know what I am doing most of the time, but I do keep an eye on mem usage, and use that pretty much all the time to ensure I’m not going high.

    The way I code (I have no experience prior to June 2011 of any kind of programming, ever) is I put everything into one main.lua file and then I write a commented table of contents, and then seperate each section with 2 lines one of —- and +++ across.

    Who knows if it’s right, but seems to be working! lol.

    With that said, I’m looking at this as something I want to learn.

    ng

    Reply
  15. kawika

    I would like to see a corona warning message when “depreciated” is indicated by the LUA version we are using.

    Reply
  16. Cheetomoskeeto

    This is so obvious and simple – I do this in Javascript all the time and it never occurred to me to use it in Lua: localize a namespace and use it! Thanks Jonathan and all for this. I was always under the impression that module was safe. Time to update the tutorials.

    Reply
  17. Kawika

    Jonathan, could you show the modified code (main.lua and examplemodule.lua) to utilize actual values between the two (rather than the print statements).

    Reply
  18. Madjack

    How do you actually calls functions or variables with this setup. I think i have everything setup like you advised, but now im confused on how to actually call a function or variable from another module. If i have a “mechanics” module, what is the code to call it? thanks

    Reply
  19. Pop

    I seem to write my modules very differently to everyone else here. I use the setfenv call to get around the worry of writing to, or creating new global variables.

    I my approach generally follows this format:

    ———–
    local M={}
    modulename=M

    setfenv(1,M)

    function moduleFunction()

    end

    return M
    ——-

    That seems to have worked well for me. It does mean I need to import all the globals I want into locals before the setfenv call (or import _G), but as a Java developer I quite like “declaring” the external modules I want to use.

    I was curious though, does anyone know whether there are any performance hits associated with this approach?

    Reply
  20. Ricardo Rauber

    That’s a very important thing! Like Cheetomoskeeto said, I was always under the impression that module was safe. Let me take a deep look on this and a lot os tests to put it on Director. If it would correct memory leaks, it will be the major update on Director 1.5.

    Thanks guys

    Reply
  21. madmusic6

    Can anyone answer this question? :

    –> Like ustimag.in.board changed director, do we have to also change ui lua, beebegames.lua, TextCandy.lua, etc (Other modules/scripts) that we use in our projects, as they have module(…, package.seeall) at the top ?

    Thanks from the UK!

    Reply
  22. dickey

    This is an important issue. Rather than jump on-board immediately with Jonathan’s suggestion (no disrespect intended as this blog post is a big step in the right direction), should we not as a community determine the best approach before the Director’s and Particle Candy’s of the Corona World and anyone else contemplating marketing ext libs make wholesale changes to their code bases?

    In recent days I have coded two projects one using Jonathan’s approach above and another using Pop’s approach (above) – but adding the line setmetatable(M, {__index = _G}) for inheritance. I am wondering after reading the LUA docs http://www.lua.org/pil/15.4.html#package-env, whether Pop’s approach is in fact the preferred one. I am studying up on the various approaches to writing externals without using module, and it seemed apparent that if one external developer chooses one method and a second developer an alternative method their products may not play well in the same project.

    Thoughts anyone?

    @Pop, the performance hit (if any) for using inheritance in the manner you describe is simply because you inherit everything in the global array as opposed to inheriting only what you need, which takes more care. I believe it makes a negligible different, but am yet to prove it one way or another.

    Kind regards, Andrew

    Reply
  23. x-pressive.com

    @Xenon: with the upcoming version 1.0.17, Particle Candy will be rewritten to get rid of the module function (usage and everything else stays the same, of course, so it won’t break any existing code). If you need the updated version asap, just drop a mail and we’ll send it over to you.

    Reply
  24. jn19

    Most of the modules I create are for display objects. How can this method you are proposing deal with that situation? How can I return the display object?

    local examplemod = require “examplemodule”
    examplemod.new()
    examplemod.x = 50 –this throws an error

    Reply
  25. madmusic6

    @jn19 did you mean:

    local examplemod = require “examplemodule”
    examplemod.new()
    examplemod.whateverDisplayObject.x = 50

    Reply
  26. jn19

    @madmusic6
    Ideally one would want to do this:

    local cat = Cat.new()
    cat.x = 50
    cat.meow()
    etc.

    Instead of

    cat.someDisplayObject.x = 50

    Reply
  27. Tom

    @jon

    There is no real different between the two methods of defining a function, except when using recursive local functions.

    With method #1, you could not call “testfunction” from inside the function because it is only defined at the end of the function.

    In method #2, “testfunction” has already been defined and visible inside the function. Lua expands method #2 to the following:

    local testfunction — forward reference
    testfunction = function(arg)
    ….
    end

    You can read more about this on page 51, Programming in Lua book.

    – Method #1
    local testfunction = function(arg)
    ….
    end

    and

    – Method #2
    local function testfunction(arg)
    ….
    end

    Reply
  28. kawika

    Could anyone provide me some feedback on receiving a value back under the new concept. I’m a little new to LUA and would appreciate some help.

    Main.lua
    ———
    local examplemod = require “examplemodule”
    local M = {}
    examplemod.testFunction1()
    print(M[1]) — nil ? Where’s the beef?

    External lua:
    ————–
    – define a local table to store all references to functions/variables
    local M = {}

    – functions are now local:
    local testFunction1 = function()
    M[1] = “very dark” ; M[2] = “dark light”; M[3] = “not dark”
    end
    – assign a reference to the above local function
    M.testFunction1 = testFunction1

    – Finally, return the table to be used locally elsewhere
    return M –return value to caller?

    Reply
  29. Kawika

    @carlos and anyone who can assist. When I debug main.lua I get: dump M[1] “nil value”

    How do I access the values in M to use them in main.lua? The values are in examplemodule.lua. Help this seems so easy, what’s happening? This easy language has now presented me with a road block in my design – to eliminate memory leaks in my software. Please send the lua swat team.

    Error in expression:
    [string "return (M[1])”]:1: attempt to index global ‘M’ (a nil value)
    stack traceback:
    [string "return (M[1])”]:1: in main chunk
    [C]: in function ‘xpcall’
    ?: in function

    Thanks for responding!

    Reply
  30. Dewey

    the module function by itself has NOTHING to do with memory “leaks”…your code does…that’s it. With the exception of “weak” tables, a reference is a reference….if your local var = require(‘somemodule’) persists for the life of your app, then the entire token space is preserved in memory regardless of whether or not a tiny pointer to the table exists in package.loaded

    This technique could actually cause WORSE memory leaks because the same code is loaded multiple times into different tables, so that if you don’t CONFIRM that you’ve deferenced your locals to allow them to be GC’d, then you will TRULY leak and VERY fast. Doing any kind of decorating or inheritance in your modules will almost guarantee you hit this problem…

    These techniques are good, but thinking they will solve a memory leak is a red-herring….furthermore, by re-requiring the same source-file over and over again, without letting Lua cache it, you are repeatedly incurring one of Lua’s most expensive operations…the parse and tokenize phase

    hope this helps…

    Reply
  31. Pixel Envision

    I just wanted to let you guys know that I have updated enhanced ui.lua (to 2.2) with this earlier today. Module function is removed… New code is available at my blog (along with the file download) and the code exchange…

    Reply
  32. XenonBL

    So I updated my own app to use the recently modified versions of ui.lua and particle candy library that have been rewritten to support this new module-less paradigm. When I had the old versions in my app I included them in main.lua with a line like ‘local ui = require( “ui” )’, but now, with the new versions, I have to use a GLOBAL require in order for them to be seen by other modules. As I mentioned above, this seems to defeat the whole “going totally local” purpose of doing away with module().

    Jon suggests I should instead locally require them in every other module that uses them, but Dewey’s comments raise a red flag that seems relevant here. If I include them in every module locally won’t I risk having many multiple versions of the same module clogging up the bloating memory usage and causing memory leaks if I’m not careful to completely remove them.

    Put in the form of a question, how can I include the same lua source file over and over, as Jon is recommending I do, but still make sure lua is caching them, as Dewey says I must do or will get major memory leaks?

    At this point I’m more confused than ever about the best approach to modular programming in lua/Corona.

    Reply
  33. Dewey

    XenonBL, all….I need to apologize; what I wrote above had a big error in it…it was quite late and I was remembering (incorrectly so) that it was MODULE rather than REQUIRE statement that cache’s the tokenized code in package.loaded. That was wrong…it IS the REQUIRE function that updates package.loaded such that multiple requires should always reference the same originally loaded copy and not leak or re-parse as I had cautioned. It should be reiterated that package.loaded is less “global” than _G is since it doesn’t exist in the scope search path for anything but another call to “require”…you are completely safe to use JB’s technique in full, but don’t expect it to solve any memory leaks for you ;_

    Reply
  34. Scott

    Is there any benefit to doing:
    local foo = something
    M.foo = foo

    as opposed to just going directly to the table and doing:
    M.foo = something

    Reply
    • Jonathan Beebe

      @Scott: The only benefit is that when you are porting existing modules (that use module), doing it in the way I showed will help reduce the chances of that module’s code breaking (if functions within that module rely on other public functions within that module). Other than that, nope, absolutely no difference really.

      Reply
  35. Nicholas Golden

    Ok, so I’m really lame when it comes to coding, never did anything before lua as I have always said. The code below works, but I’m probably either just lucky, or it’s messy.

    am I interpreting this correctly?

    “local M = {} stores all references to functions and variables”

    How does it do that? Does that mean any function I write will always be stored in M. Maybe I should ask that afterwords, but to my first part of the question is the below correct even though it “works”?

    I’m really trying to establish good practices since I had no coding experience before. I know that just because things work, doesn’t mean it’s good. haha,

    let the fun begin!


    -- define a local table to store all references to functions/variables

    local M = {}

    -- functions are now local:

    local redbox = display.newRect (130, 50, 60, 50)
    redbox:setFillColor(255,0,0)

    local greenbox = display.newRect (130, 150, 60, 50)
    greenbox:setFillColor(0,255,0)

    local bluebox = display.newRect (130, 250, 60, 50)
    bluebox:setFillColor(0,0,255)

    local testFunction1 = function(event)
    if event.phase == "began" then
    redbox.alpha = .25
    print( "Test 1 - red box faded" )
    end
    end

    -- assign a reference to the above local function

    M.testFunction1 = testFunction1

    local testFunction2 = function(event)
    if event.phase == "began" then
    greenbox.alpha = .25
    print( "Test 2 - green box faded" )
    end
    end

    M.testFunction2 = testFunction2

    local testFunction3 = function(event)
    if event.phase == "began" then
    bluebox.alpha = .25
    print( "Test 3 - blue box faded" )
    end
    end

    M.testFunction3 = testFunction3

    -- Finally, return the table to be used locally elsewhere

    redbox:addEventListener("touch", testFunction1)
    greenbox:addEventListener("touch", testFunction2)
    bluebox:addEventListener("touch", testFunction3)

    return M

    Reply
  36. Nicholas Golden

    Oh and one more question to the above (no edit button….sadness)

    why is it the eventlisteners in my code only work when placed before “return M” and not after?

    Sorry for the dummy question. I do have books and I am reading them (lua reference and programming in lua 2nd edition) but…yea im noob.

    ng

    Reply
  37. Brent Sorrentino

    Excellent thread! But I have some questions about what functions to “include” in the new local “M” table.

    Let’s say that I have an external module”popupwindow.lua”. In that module are 3 functions, but only 2 of the modules are “called” from elsewhere, i.e. from main.lua. For example…


    --popupwindow.lua
    local function pauseContent() (my code) end

    local function clearContent() (my code) end

    local function loadContent() (my code) end

    Of these 3 functions, the only function that needs to be “accessible” from outside this module is “loadContent”, and so I would include it in the table “M” as specified…


    --popupwindow.lua
    local M = {}

    local function pauseContent() (my code) end

    local function clearContent() (my code) end

    local function loadContent() (my code) end
    M.loadContent = loadContent

    return M

    This, of course, makes “loadContent” accessible from any other Lua file which requires-in “popupwindown.lua”. But what about the other two functions in the module? They are used only internally within that module… never outside it… but do I still need to add them to table “M” to prevent them from going into the global space?

    Expand this outward to a theoretical module with 15 local functions. If only 3 of those functions need to be accessible from elsewhere, what about the other 12? It seems very tedious if I must include all 12 functions inside “M” even if they’re never used outside the module… but if this is necessary for true optimization, so be it. I’m just trying to gain a better understanding of what’s going on under the hood.

    Thanks!
    Brent Sorrentino
    Ignis Design

    Reply
  38. Jonathan Beebe

    @Brent: If you don’t want a function (or any variable for that matter) to be publicly accessible to other modules, simply declare it as ‘local’ in your module. That’ll prevent it from being global, and as long as it’s not in the ‘M’ table, it won’t be accessible by other modules either.

    Hope that helps!

    Reply
  39. Brent Sorrentino

    Thanks Jonathan! That answers my question exactly. I was hoping that the usage of “require(”)” didn’t somehow place everything in the ‘required-in’ file into the global scope… turns out the culprit was Lua’s “module(…)” function, and I’m pleased that they’re depreciating it. I managed to purely localize my entire app using the new method and it didn’t take long at all.

    Out of curiosity, since you are in closer contact with Ansca, will they depreciate the “module” function in Corona’s core libraries such as the physics and sprite libraries? I don’t know how these are programmed into Corona’s architecture behind the scenes (I don’t really want to know, ha ha), but since the physics/sprite/etc. libraries are required-in, I’m concerned that they utilize the module function, thus placing all associated functions into the global scope.

    However I don’t pretend to understand the core architecture behind Corona, and I leave it to Ansca’s good judgment to do what’s necessary regarding the depreciation of “module(…)”.

    Brent

    Reply
  40. Bob

    So how do you get a constructor out of this new setup?

    Say I want to be able to do this do this:

    local b1 = newBall(“red”);
    local b2 = newBall(“blue”);

    The only way I have got this going without modules so far is to do this:
    function newBall(_color)

    local ball = {}

    ball.color = _color
    ball.state = “full”

    local bounce = function()
    print(“bouncing”);
    end
    ball.bounce = bounce

    local empty = function()
    ball.state = “empty”;
    end
    ball.empty = empty

    — Finally, return the table to be used locally elsewhere
    return ball
    end

    And my main.lua file looks like this , that I actually tested with:

    local BallFactory = require(“Ball”)

    local b1 = newBall(“red”)
    local b2 = newBall(“blue”)

    –Make sure we have two distinct objects created
    print(b1.color)
    print(b2.color)
    print(b1.color)
    print(b2.color)

    b1:bounce()
    print(b1.state)
    b1:empty()
    print(b1.state)

    Thanks
    Bob

    Reply
  41. Bob

    Appears the scope of is global this
    local BallFactory = require(“Ball”)
    or require(“Ball”) will work when I run my main.lua file. Is this bad? Is that the beast I’m trying to defeat?

    Reply
  42. madmusic6

    How can you clear memory?
    If a menu page memusage is say 200, then memusage goes up to say 350 after changing scenes, surely it should go back to 200 when changing back to menu page.
    Here is an example, has anyone got a way to clear the information held by require?

    -- main.lua
    display.setStatusBar( display.HiddenStatusBar )

    local menu = require( "menu" )

    menu.runMenu()

    --menu.lua
    local menuTab ={}

    menuTab.runMenu = function()
    menuGroup = display.newGroup()

    local monitorMem = function()
    collectgarbage("collect")
    print( "nMemUsage: " .. collectgarbage("count") )
    local textMem = system.getInfo( "textureMemoryUsed" ) / 1000000
    print( "TexMem: " .. textMem )
    end

    local closeMenu = function()
    display.remove(Atext)
    Atext = nil
    display.remove(Atext2)
    Atext2 = nil
    display.remove(menuGroup)
    menuGroup = nil
    shez1 = require("shez1")
    shez1.runShez1()
    timer.performWithDelay(2, function() collectgarbage("collect") end)

    end

    local Atext = display.newText( "Goto shez1",
    display.viewableContentWidth*0.25,
    display.viewableContentHeight*0.44,
    native.systemFontBold, 36)
    Atext:setTextColor(255, 255, 255)
    menuGroup:insert(Atext)
    Atext:addEventListener( "tap", closeMenu)

    local Atext2 = display.newText( "Press for memory",
    display.viewableContentWidth*0.25,
    display.viewableContentHeight*0.64,
    native.systemFontBold, 36)
    Atext2:setTextColor(255, 255, 255)
    menuGroup:insert(Atext2)
    Atext2:addEventListener( "tap", monitorMem)

    end

    return menuTab

    --shez1.lua
    local shezTab ={}

    shezTab.runShez1 = function()
    shez1Group = display.newGroup()

    local closeShez = function()
    display.remove(Btext)
    Btext = nil
    display.remove(shez1Group)
    shez1Group = nil
    local menu = require("menu")
    menu.runMenu()

    timer.performWithDelay(2, function() collectgarbage("collect") end)

    end

    local Btext = display.newText( "Goto Menu",
    display.viewableContentWidth*0.25,
    display.viewableContentHeight*0.44,
    native.systemFontBold, 36)
    Btext:setTextColor(255, 255, 255)
    shez1Group:insert(Btext)
    Btext:addEventListener( "tap", closeShez)
    end

    return shezTab

    Am I worrying too much about this memory staying there?
    I have tried different scene changing libs (director etc) but always get the same problem.
    Many Thanks.

    Reply
  43. Nevin Flanagan

    Because there was some question raised above, regarding the use of “local foo = function()” vs. “local function foo()”, I compiled both versions to Lua bytecode. The outputs were literally identical.

    [code]-- define a local table to store all references to functions/variables
    local M = {}

    -- functions are now local:
    local testFunction1 = function()
    print( "Test 1" )
    end
    -- assign a reference to the above local function
    M.testFunction1 = testFunction1

    local testFunction2 = function()
    print( "Test 2" )
    end
    M.testFunction2 = testFunction2

    local testFunction3 = function()
    print( "Test 3" )
    end
    M.testFunction3 = testFunction3

    -- Finally, return the table to be used locally elsewhere
    return M[/code]

    [code]-- define a local table to store all references to functions/variables
    local M = {}

    -- functions are now local:
    local function testFunction1()
    print( "Test 1" )
    end
    -- assign a reference to the above local function
    M.testFunction1 = testFunction1

    local function testFunction2()
    print( "Test 2" )
    end
    M.testFunction2 = testFunction2

    local function testFunction3()
    print( "Test 3" )
    end
    M.testFunction3 = testFunction3

    -- Finally, return the table to be used locally elsewhere
    return M

    [/code]

    Reply
  44. Tom

    @Nevin,

    You’re right that the two ways of defining the functions are identical EXCEPT if you need to call the function within the same function (recursive call).

    The method: local function testfunction(arg) is callable within the defined function where local testfunction = function(arg) is not.

    Reply
  45. Mohammad

    I have performance issues after changing my code in this way @Jonathan Beebe

    everything works fine but the problem is performance is too slow in real device (android os ) my old code on real device in module run fast also 30 fps is working correctly but after changing my code in that way the game start with 15 fps and memory is the same usage please help me if there is way to know where is the memory leaks in my code so i can change it ..

    thanks in advance

    Reply
  46. Darren

    This technique reminds me of the Asynchronous Module Definition used in modern JavaScript libraries. However, since “require()” assigns “package.loaded[modulename]” to the loaded module’s exports this means that all modules are loaded into the same “context” (i.e. the global context — ‘package.loaded’). A more robust approach would have support for contexts, such that modules can import/require other modules into a new context, thereby allowing different versions of modules to coexist.

    For a module that permits loading context aware modules see my Context Lua module ( https://github.com/dschnare/Context-Lua. ) and its example.

    Regarding Mohammad’s performance issues: The modules that are “required/imported” are loaded and executed if and only if they haven’t been loaded already in the loading context (without context-lua this will always be the global context). Requiring a module more than once will simply return its exports, which is much faster than having to load and execute every time. Can you offer an example where this is happening for you?

    Perhaps Lua does some code optimization for modules defined using the “module()” function, which may explain why code using said the “module()” function appears to execute faster. Does anyone know anything about this?

    Reply
  47. John Conti

    Chapter 15 of the Lua book explains how the module function is derived from the code shown above. After going through that chapter I have the feeling that the module function really is what we want. Am I missing something?

    Reply
  48. jack0088


    local M = {}
    function M:testFunction2()
    print( "Test 2" )
    end
    return M

    1.) Is this function local to M={} or to the file itself, OR is it global at all?
    2.) So, do I correct understand, that this function is a branch of M={}?
    2.2) Where does it belong to in the M table? Is it like M={testFunction2()} or like M={testFunction2 = ()} or M={testFunction2 = testFunction2()}??

    Reply
    • Jonathan Beebe

      @jack0088: Correct, M:testFunction2() is the same as M.testFunction2( self ) … so yes, testFunction2 is local because it belongs to ‘M’, and ‘M’ is local.

      Reply
    • tetu

      Srdjan,

      if the function in main is global then _G.funname() in the module does the job
      if you don’t want to have a global function, you can add a runtime listener in main and dispatch an event in the module

      Reply
  49. T.ALberga

    Would you also add variables this way?
    And how would you do that?

    Adding functions is working, but I keep getting nil errors if I do this with variables.

    Reply
  50. Olivier

    Hi,
    Great tutorial, thanks Jonathan.

    A question : Why don’t we use this technic … :

    – define a local table to store all references to functions/variables
    local M = {}
    ….
    – assign a reference to the above local function
    M.new = new
    – Finally, return the table to be used locally elsewhere
    return M

    …for Storyboard?

    Thank you for your answer
    Olivier

    Reply
  51. Martin

    Hi, I just started using Corona and stumbled on this page when looking for better organization of our code.
    Does this modular approach still hold in the current builds? Or is it not necessary anymore?

    Reply
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
lua学习之table类型(转)
Lua中的模板与module函数
【笨木头Lua专栏】基础补充18:Lua的模块编写与module函数 | 笨木头与游戏开发
SAP CRM settype的创建,背后发生了什么
Python Module
lua中table如何安全移除元素
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服