Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
How to get a list of a class's instances? — Gideros Forum

How to get a list of a class's instances?

MellsMells Guru
edited June 2012 in General questions
Hi,

how do you get a list of a class's instances?
-- Example
Char = Core.class(Sprite)
 
local boy = Char.new()
local girl = Char.new()
 
-- Later how can I get an array containing boy & girl objects, from Char? (Let's pretend I have no way to store it manually)
-- Something like :
local charArray = Char:getInstances()
Thank you :)
twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps

Comments

  • ar2rsawseenar2rsawseen Maintainer
    I don't think there's a native method to do it, but you can try to implement it.
    local instances = {}
    Char = Core.class(Sprite)
     
    function Char:init()
        instances[#instances+1] = self
    end
     
    function Char.getInstances()
        return instances
    end
    and then somewhere else, on other file, etc
    local boy = Char.new()
    local girl = Char.new()
     
    local charArray = Char.getInstances()
    Didn't test it, but should work. :)

    Likes: Mells, atilim

    +1 -1 (+2 / -0 )Share on Facebook
  • You just creating a variation of the factory pattern, but storing a reference to each object that you return.

    However you need to be aware that as you now have an extra reference to each object they won't ever be garbage collected unless you explicitly nil out the references - you might be better off making a pool of objects at the start of your game and then having a flag in the object to say which entries in the pool are currently in use.
    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
  • That's everything I needed to know, thank you :)
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • @techdojo
    1. When does the garbage collector do the work for me?
    local i, j
    for i, j in pairs (myArray) do
         print (i, j)
    end
    • The garbage collector collects i, j from my understanding, right?
    • Is it running after a table.remove(table, position)?
    What would be common cases where I should acknowledge the fact that the garbage collector is doing the work for me, so I can know how to act if I need to nil out objects accordingly?


    Also I have another question :

    2. What happens in the following case?
    boy:addChild(boy1) 
    girl:addChild(girl1)
    as you now have an extra reference to each object they won't ever be garbage collected unless you explicitly nil out the references
    Does it mean that childs (boy1, girl1) of those objects that are referenced in the instances array (boy, girl) won't be garbage collected either?
    In your note, do references apply to instances only or also to their childs?
    When an object is referenced, does it mean a reference is created for their child also?

    Thanks
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • Hi Mells,
    In the case of your first code, as soon as i and j go out of scope (ie the block or closure they're declared in ends) the space allocated to them will be available for collection.

    Likewise if they are references to tables or other structures then THAT memory will still remain allocated whilst a reference to it exists - a good idea is always to explicitly set the reference to "nil" when you've finished with it.

    When the reference variable goes out of scope (and is available to be collected) the memory it's pointing to is also considered "free" and so under "normal" circumstances the whole process will manage itself - eventually! Setting the reference variables to "nil" just gives the collector an extra "heads up" as to what's free and what's not.

    You can't predict exactly when the GC will run - it's run as a background process periodically, however you can force a call (or two) using the collectGarbage("collect") function.

    In all "managed" apps, dynamic memory allocation is quite an expensive process (in terms of processing power / bandwidth used) and should be done sparingly - for instance NEVER allocate locals inside for loops as a new var will be allocated each time round the loop and you'll get a massive memory hit and then the GC will have a lot of work to do (and can often cause stutter in your game).

    It's my understanding that ANY and ALL variable declarations, functions, closures, tables etc all go through the managed memory system and will eventually get "collected" as soon as they're available.

    Again my understanding is that "references" are just pointers (in the C/C++ sense) when you "store" a reference to a block of memory it's reference counter goes up and while the counter > 1 then it'll never get collected.

    In your second example (assuming both boy and girl are Sprite's - actually a table) then a reference to boy1 and girl1 are stored which will prevent them (or anything they reference) from being collected until boy and girl themselves are eligible for collection.

    Hope this helps - let me know if there's anything else you'd like me to clarify.

    Jon...

    Likes: Mells

    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
    +1 -1 (+1 / -0 )Share on Facebook
  • MellsMells Guru
    edited June 2012
    @techdojo Thanks for the long answer!
    for instance NEVER allocate locals inside for loops
    I can see how
    for i, j in pairs(myArray) do
        local number = 0
        -- (...)
    end
    Should be
    local number
    for i, j in pairs(myArray) do
        number = 0
        -- (...)
    end
    But what about the following?
     
    local function printName(self)
         -- Here
         *local* name = self.name
         print (name)
    end
     
    local boy = Sprite.new()
    boy.name = "Harrison"
     
    local girl = Sprite.new()
    girl.name = "Daryl"
     
    local chars = {boy, girl}
    for i, j in pairs(chars) do
         printName(j)
    end
    Here I am allocating locals inside a for loop, but I have read in many places that it's better to work with locals inside functions.

    Does your note apply in that case or is it different?

    Side note to myself : this community is amazing
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • techdojotechdojo Guru
    edited June 2012
    Your right locals are a LOT quicker to use than global vars, however you can declare locals outside of the scope of functions and they remain local to that chunk (or module, class or whatever you call it).

    In a trivial example like you've specified there it might be easier to write it like this...
    local function printName(self)
           print(self.name) 
    end
    However that's just silly and I think you probably mean
    local privateName = nil
    local function printName(self)
             privateName = self.name
             print(privateName)
             privateName = nil      -- prevent the last used record from having a trailing reference
    end
    In the end it comes down to where the code is placed, if your calling it from within your main "OnEnterFrame" loop or inside a loop that's called 1,000,000+ times then it might be worth it, otherwise it might be an optimisation too far. :)

    Actually - come to think of it (and not having looked through the lua source or actually asked a lua expert that has), just declaring a local like that inside a function might be the equivalent of declaring a local in C (ie it just reserves space on the stack) and so it might be a moot point after all.

    The best way to be sure once and for all would be to put the code in a big ass loop and then print out value returned from the garbage collector (watching that the print statement doesn't also allocate memory as well)

    ie.
    local mf = math.floor            -- another good habit to get in to! localising references to library functions
    local function loopy(a)
        local b=10+a
        return b
    end
     
    for i=1,100000 do
       loopy(i)
       print(mf(collectgarbage("count")))
    end

    Likes: Mells

    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
    +1 -1 (+1 / -0 )Share on Facebook
  • atilimatilim Maintainer
    edited June 2012
    On the other hand, in Lua, numbers and booleans are very primitive types and they aren't treated as GCable objects. Therefore, you should only be careful about tables, strings and functions.

    Also strings are hashed and stored uniquely in memory. Therefore these 3 strings are stored at the same memory location:
        local str1 = "my string"
        local str2 = "my string"
        local str3 = "my".." ".."string"

    Likes: Mells

    +1 -1 (+1 / -0 )Share on Facebook
  • ScouserScouser Guru
    edited June 2012
    I think you will find that is covered in @techdojo's first paragraph
    In the case of your first code, as soon as i and j go out of scope (ie the block or closure they're declared in ends) the space allocated to them will be available for collection.
    name will lose scope when printName returns. Funny thing is I was reading something about this only about half an hour ago..

    *edit* Doh!! Ninja'd twice :(

    Likes: Mells

    +1 -1 (+1 / -0 )Share on Facebook
  • On the other hand, in Lua, numbers and booleans are very primitive types and they aren't allocated as GCed objects. You should only be careful about tables, strings and functions.
    I thought as I was posting, that it might be the case, I'm assuming that a "reference" is also a primitive type, it's just what it references that potentially requires allocating.

    *sheesh* and they said memory management in C++ was hard! :)

    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
  • atilimatilim Maintainer

    *sheesh* and they said memory management in C++ was hard! :)
    :D exactly! While programming I always find myself thinking about "this reference should be weak or not or weak or not..." :)
  • MellsMells Guru
    edited June 2012
    Great answers again, love it.

    @techdojo
    I know I ask a lot today sorry, I'm just a few weeks from the beginning of my learning (even though I had done a bit of programming with another framework, I had to stop soon) so please bear with me while I'm learning.
    However that's just silly and I think you probably mean
    local privateName = nil
    local function printName(self)
             privateName = self.name
             print(privateName)
             privateName = nil      -- prevent the last used record from having a trailing reference
    end
    Yes, I was thinking about something more complex.
    I was trying to avoid putting too many locals outside of my functions for example :
    function myClass:doSomething(myList, myGroup, defaultProperties)
         local myList = myList
         local myGroup = myGroup
         local defaultProperties = defaultProperties
         -- many lines of code below
    end
    because if I had to put local declaration outside of all my class functions, I would end up with something really messy.

    @atilim > Interesting, I hadn't come across that information yet (lua-users and other sites that I've read the last few weeks).

    @Scouser > If I understand well you mean that name will loose scope and so be collected. But on the other hand it doesn't help if what we want to avoid is recreating this local for each loop. Do I get it well?
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • Great answers again, love it.

    @techdojo
    I know I ask a lot today sorry, I'm just a few weeks from the beginning of my learning (even though I had done a bit of programming with another framework, I had to stop soon) so please bear with me while I'm learning.
    No worries, we were all beginners once, that's what's so great about internet forums, you can turn up, ask questions and actually learn something! :) it's just like being connected to the biggest information resource in the world (oh hang on a minute it's exactly like that :) )

    Personally, I'm always ready to help anyone who's prepared to help themselves. What I hate is forums where some noob just pops up and say's "waaa this is too hard, will someone do my homework for me", making the effort to post a question, ask intelligent follow up questions and being prepared to experiment is more than enough for me to get involved (if I can) :)

    Likes: Mells, atilim

    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
    +1 -1 (+2 / -0 )Share on Facebook
  • atilimatilim Maintainer
    @Mells
    As a side note, the code:
    function myClass:doSomething(myList, myGroup, defaultProperties)
         local myList = myList
         local myGroup = myGroup
         local defaultProperties = defaultProperties
         -- many lines of code below
    end
    doesn't improve efficiency because accessing the function parameters is as fast as accessing the local variables.
  • atilimatilim Maintainer
    edited June 2012
    @ar2rsawseen, then it's possible to extend your idea as:
    Char = Core.class(Sprite)
    Char.instances = setmetatable({}, {__mode = "k"})
     
    function Char:init()
        -- not using arrays here because collection of an object creates holes in the array
        Char.instances[self] = true 
    end
     
    function Char.getInstances()
        return Char.instances
    end
     
    local chars = Char.getInstances()
    for char in pairs(chars) do
        --
    end
Sign In or Register to comment.