Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
a newbie question — Gideros Forum

a newbie question

I need your help please. Is this code correct?
local textfield = nil
-- texfield 1
textfield = TextField.new(nil, "my first text")
self:addChild(textfield)
-- texfield 2
textfield = TextField.new(nil, "my second text")
self:addChild(textfield)
Will both textfields use the same local variable?
Is it good practice to do this?
Thank you.
my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
Tagged:

Comments

  • PaulHPaulH Member
    Accepted Answer
    If you don't plan to access the textfields after you've added them to a sprite, generally I don't think that's harmful. Just be aware of what variables have been reused. Suppose you wrote:

    function add_text_to_sprite(sprite)
    local textfield1 = TextField.new(...)
    sprite:addChild(textfield1)
    local textfield2 = TextField.new(...)
    sprite:addChild(textfield2)
    end
    add_text_to_sprite(my_sprite)

    Since the two text fields are local variables, local to the function, the variables are gone after the function returns, but the text fields remain as children of my_sprite. There's no harm in doing the same with a single variable:

    function add_text_to_sprite(sprite)
    local textfield = TextField.new(...)
    sprite:addChild(textfield)
    textfield = TextField.new(...)
    sprite:addChild(textfield)
    end
    add_text_to_sprite(my_sprite)

    That has exactly the same effect. The more important factor is to use a variable that's local to the scope where you need it.

    In your example, when you create the second TextField, you're reusing the variable you created for the first one. The only way you'd be able to access the first one again would be to get the child of the Sprite you added it to. Suppose later you wanted to change the text in the first one. If you wrote:

    textfield:setText("new text")

    that would change the text of the second one created. To change the text in the first one you'd have to do something like:

    textfield = self:getChildAt(1)
    textfield:setText("new text")

    Which only works if you're keeping track of the number of children in the sprite where you added it, so you know which child is the TextField you want to change.

    So I'd say feel free to reuse variable names (there can even be memory advantages to doing so) but only when you know you won't need to access whatever it was the variable originally referred to again.

    Paul

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • MoKaLuxMoKaLux Member
    edited December 2019
    PaulH said:

    If you don't plan to access the textfields after you've added them to a sprite, generally I don't think that's harmful.

    I don't need to change the text so that's cool. I am using it in a for loop.
    PaulH said:

    So I'd say feel free to reuse variable names (there can even be memory advantages to doing so)

    That's exactly what I am after: optimisation (even if it's not a lot). I wanted to make sure that the second textfield was using the same local variable.

    Thank you very much for your detailed answer, very much appreciated.
    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
  • Apollo14Apollo14 Member
    edited December 2019
    btw 'nil' is not necessary when you declare locals
    local textfield = nil --not necessary
    you can declare it just like that:
    local textfield

    Likes: MoKaLux, oleg

    > Newcomers roadmap: from where to start learning Gideros
    "What one programmer can do in one month, two programmers can do in two months." - Fred Brooks
    “The more you do coding stuff, the better you get at it.” - Aristotle (322 BC)
    +1 -1 (+2 / -0 )Share on Facebook
  • rrraptorrrraptor Member
    edited December 2019
    I did a quick tests to show that if you store references to objects in tables, then you have to clear those references after the object is destroyed. But its not always the case :)
    In all tests I'm creating a grid of textfileds.

    test1: create textfiled, add it to scene, add it to local table (t).
    test2: create textfiled, add it to scene
    test3: create textfiled, add it to scene, add it to global table (globalTable).
    test4: create textfiled, add it to scene, add it to scene table (scene.t).

    Tweak CLEAR_GLOBAL_TABLE and DELETE_SCENE "variables" to see how they affect memory usage :)

    CLEAR_GLOBAL_TABLE @ true
    DELETE_SCENE @ true
     
    NUMBER_OF_TESTS @ 4
    EPSILON @ 0.01
     
    SCALE @ 4
    OFFSET @ 10
     
    local scene
    local globalTable
    local initMemUsage
     
    function getMem(msg, printLine, symbol)
    	if (printLine) then 
    		symbol = symbol or "-"
    		print(string.rep(symbol, 35))
    	end
    	-- force gc to collect garbage
    	for i = 1, 4 do collectgarbage() end
    	-- convert kbytes to megabytes 
    	local mb = collectgarbage("count")/1024
    	print(msg, string.format("Memory: %f mb", mb))
    	return mb
    end
     
    function runTest(n)
    	scene = Sprite.new()
    	stage:addChild(scene)
     
    	local memory = getMem(string.format("Initializing test %i", n), true)
    	_G["test"..n]() -- call test function (test1, test2, ...)
     
    	local diff = (collectgarbage("count")/1024) - memory -- calculate memory usage used by test
    	print(string.format("Test %i - %f mb", n, diff))
    	memory = getMem(string.format("	Before cleaning test %i",n)) -- print memory usage before cleaning up everything
    	local approx = memory - diff
    	print(string.format("	Approximate memory usage after cleaning up: %f", approx)) 
    	scene:removeFromParent()
    	if (DELETE_SCENE) then 
    		scene = nil
    	end
    	local cleaned = getMem(string.format("	Cleaning after test %i", n))
     
    	if (not(cleaned>= approx-EPSILON and cleaned<=approx+EPSILON)) then 
    		print("!!! Posible memory leak!!!")
    	end
     
    	-- if next text exists, run it
    	if (n+1 <= NUMBER_OF_TESTS) then 
    		runTest(n+1)
    	-- otherwise output some info
    	else
    		if (CLEAR_GLOBAL_TABLE) then 
    			globalTable = nil
    		end
    		local finishedMemUsage = getMem("All tests finished.", true)
    		if (not(finishedMemUsage >= initMemUsage-EPSILON and finishedMemUsage <= initMemUsage+EPSILON)) then 
    			print("!!!Memory leak!!!")
    		end
     
    		print(string.format("scene is %s deleted", type(scene) == "nil" and "" or "not"))
    		print(string.format("globalTable is %s deleted", globalTable == nil and "" or "not"))
    	end
    end
     
    function test1()
    	local t = {}
    	for y = 1, 11 do 
    		t[y] = {}
    		for x = 1, 20 do 
    			local tf = TextField.new(nil, tostring(math.random(9)), "|")
    			tf:setScale(SCALE)
    			tf:setPosition(x * (30 + OFFSET), y * (30 + OFFSET))
    			scene:addChild(tf)
    			t[y][x] = tf
    		end
    	end
    end
     
    function test2()
    	for y = 1, 11 do 
    		for x = 1, 20 do 
    			local tf = TextField.new(nil, tostring(math.random(9)), "|")
    			tf:setScale(SCALE)
    			tf:setPosition(x * (30 + OFFSET), y * (30 + OFFSET))
    			scene:addChild(tf)
    		end
    	end
    end
     
    function test3()
    	globalTable = {}
    	for y = 1, 11 do 
    		globalTable[y] = {}
    		for x = 1, 20 do 
    			local tf = TextField.new(nil, tostring(math.random(9)), "|")
    			tf:setScale(SCALE)
    			tf:setPosition(x * (30 + OFFSET), y * (30 + OFFSET))
    			scene:addChild(tf)
    			globalTable[y][x] = tf
    		end
    	end
    end
     
    function test4()
    	scene.t = {}
    	for y = 1, 11 do 
    		scene.t[y] = {}
    		for x = 1, 20 do 
    			local tf = TextField.new(nil, tostring(math.random(9)), "|")
    			tf:setScale(SCALE)
    			tf:setPosition(x * (30 + OFFSET), y * (30 + OFFSET))
    			scene:addChild(tf)
    			scene.t[y][x] = tf
    		end
    	end
    end
     
    print(string.format([[Settings:
    	CLEAR_GLOBAL_TABLE - %s
    	DELETE_SCENE - %s
    ]], tostring(CLEAR_GLOBAL_TABLE), tostring(DELETE_SCENE)))
    initMemUsage = getMem("Initial state.", true)
    runTest(1)

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • @rrraptor this was supposed to be a noob question :o
    I am going to give it a try and see what I've got. Thank you for your help.
    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
  • rrraptorrrraptor Member
    edited December 2019 Accepted Answer
    @MoKaLux just keep in mind that if you have refence to object and when you dont need that object you need to remove reference AND the object itself (like sprite:removeFromParent() myRefernceValue = nil) :)
    But I guess, if you are using SceneManager, then it should not be a problem.
  • Apollo14Apollo14 Member
    Accepted Answer
    and I've noticed that sometimes objects never get garbagecollected automatically, even if they're already nil and removed from parents

    but after manual 'collectgarbage()' call they get collected
    > Newcomers roadmap: from where to start learning Gideros
    "What one programmer can do in one month, two programmers can do in two months." - Fred Brooks
    “The more you do coding stuff, the better you get at it.” - Aristotle (322 BC)
  • Apollo14 said:

    and I've noticed that sometimes objects never get garbagecollected automatically, even if they're already nil and removed from parents

    but after manual 'collectgarbage()' call they get collected

    Lua uses a garbage collector that runs from time to time to collect dead objects when they are no longer accessible from the Lua program.
    So it won't be collected right away (unless you will force gc to collect).
  • olegoleg Member
    Accepted Answer
    Apollo14 said:

    and I've noticed that sometimes objects never get garbagecollected automatically, even if they're already nil and removed from parents

    but after manual 'collectgarbage()' call they get collected

    All object references must be deleted so that they are automatically deleted.

    If you delete the link to the parent sprite, you need to collect garbage twice to collect his children

    Likes: Apollo14

    my games:
    https://play.google.com/store/apps/developer?id=razorback456
    мій блог по гідерос https://simartinfo.blogspot.com
    Слава Україні!
    +1 -1 (+1 / -0 )Share on Facebook
  • SinisterSoftSinisterSoft Maintainer
    Accepted Answer
    I do this at the end of my main.lua
    collectgarbage("setstepmul",1000)
    collectgarbage()
    And this on my main enter frame event:
    collectgarbage("step")
    --print(collectgarbage("count")*1024)
    This means the collection is done regularly but not too much at once. You should tweak the 1000 for your games but it's a good starting point. The count give you an indication of which direction you should be going in.

    Likes: oleg

    Coder, video game industry veteran (since the '80s, ❤'s assembler), arrested - never convicted hacker (in the '90s), dad of five, he/him (if that even matters!).
    https://deluxepixel.com
    +1 -1 (+1 / -0 )Share on Facebook
  • I used to call collectgarbage() fairly often in my code. Now I usually on call it if I'm changing between scenes, something that doesn't happen in real time, and involves getting rid of a lot of Sprites. It might be my imagination, but automatic garbage collection seems to be working better than it once did, or perhaps I've gotten better at using local variables, so I don't accidentally leave references around for things that are no longer needed.

    In the 3D asteroid sample, I'm never calling collectgarbage(). New asteroids are created every few frames, and each gets added to a sprite in the scene. No variable is kept that references it. When the asteroid moves out of sight behind the camera, or gets destroyed, I remove it from its parent. When the ship is destroyed it creates 500 meshes for fragments of the ship, and each of those gets removed from the scene after a certain number of frames. Each asteroid destroyed creates 50 fragments that behave the same way. In theory, the memory for all those asteroids and fragments could get freed up anytime after they've been removed from the scene.

    I was curious to see if automatic garbage collection would keep up with all the asteroids and fragments that have been removed from the scene, or if the memory usage would continually climb. It seems the automatic collection handles this just fine. I can leave the game running for an extended period of time and the memory usage stays within the usual range.

    Still, calling collectgarbage() periodically is probably a wise practice in general, especially using the method @SinisterSoft suggested.
    +1 -1 (+3 / -0 )Share on Facebook
  • @SinisterSoft , no need to test, i think he failed the turing test basically in each of his posts.
Sign In or Register to comment.