Error setting an array object to nil

I’m building a children’s book that has a page turn effect. This is to be viewed in landscape mode only.

The pages are loaded into two arrays, “oddPgs” and “evenPgs”. The first two pages are loaded at start up with the right page (page 1, the cover) loaded into the “oddPgs” array at position 1 and being displayed on the right half of the screen. The left page (page 2) is loaded into the “evenPgs” array at position 1 and displayed at the far left of the screen with a width of 0.

When page 1 is clicked it transitions to a width of 0 while page 2 transitions to the left half of the screen while it “unfurls” to it’s full width. The next 2 pages are loaded into the “oddPgs” and “evenPgs” arrays.

Once 4 pages have been loaded I begin removing the pages that are no longer needed. These would be the even page “evenPgs[2]” below the currently displayed even page and the odd page “oddPgs[3]” that has been made transparent.

My problem is i get the error “WARNING: Attempting to set property(2) with nil” when I try to give “evenPgs[2]” a nil value. Everything else seems to work fine. I have read and re-read the section on using arrays but do not understand why the lines:

oddPgs:remove(oddPgs[3])
oddPgs[3] = nil

works but the lines:

oddPgs:remove(evenPgs[2])
evenPgs[2] = nil
gives an error.

Here is the complete code:

-- Load the relevant LuaSocket modules (no additional files required for these)  
local http = require("socket.http")  
local ltn12 = require("ltn12")  
  
-- load external libraries (should be in the same folder as main.lua) --------------------  
local sprite = require("sprite")  
--local animate = require("animations")  
  
display.setStatusBar( display.HiddenStatusBar )  
  
-- Page Flip Variables -------------------------------------------------------------------  
local pgTurn = "page-flip-2.mp3" -- Sound of page turning  
local turnSpeed = 800  
local fullH = display.contentHeight  
local fullW = display.contentWidth  
local halfW = display.contentWidth / 2  
  
-- Add Display Groups ----------------------------------------------------------  
local oddPgs = display.newGroup()  
local evenPgs = display.newGroup()  
local aniBtns = display.newGroup()  
local foreground = display.newGroup()  
  
-- Parse images into page array--------------------------------------------------  
local pages = {"Page01.jpg", "Page02.jpg", "Page03.jpg", "Page04.jpg", "Page05.jpg", "Page06.jpg", "Page07.jpg", "Page08.jpg", "Page09.jpg", "Page10.jpg", "Page11.jpg", "Page12.jpg", "Page13.jpg", "Page14.jpg", "Page15.jpg", "Page16.jpg", "Page17.jpg", "Page18.jpg", "Page19.jpg", "Page20.jpg", "Page21.jpg", "Page22.jpg", "Page23.jpg", "Page24.jpg", "Page25.jpg", "Page26.jpg", "Page27.jpg", "Page28.jpg", "Page29.jpg", "Page30.jpg", "Page31.jpg", "Page32.jpg", "Page33.jpg", "Page34.jpg", "Page35.jpg", "Page36.jpg", "Page37.jpg", "Page38.jpg", "Page39.jpg", "Page40.jpg", "Page41.jpg", "Page42.jpg", "Page43.jpg", "Page00.jpg"}  
local image = {} -- Create the image array  
local ttlPgs = #pages -- Get the total number of objects in the array  
  
-- Add 1st even pags to display ---------------------------------------------------------  
 image[2] = display.newImage(pages[2], fullW, 0) -- Display 1st Even page  
 evenPgs:insert(1, image[2])  
 image[2]:setReferencePoint(display.TopLeftReferencePoint) -- Use the top left reference  
 image[2].x = fullW  
 image[2].y = 0  
 image[2].xScale = 0.00001 -- Set the width to 0  
  
-- Add 1st odd pags to display ---------------------------------------------------------  
 image[1] = display.newImage(pages[1], halfW, 0) -- Display 1st Odd page  
 oddPgs:insert(1, image[1])  
 image[1]:setReferencePoint(display.TopLeftReferencePoint) -- Use the top left reference for positioning  
 image[1].x = halfW  
 image[1].y = 0  
  
-- Page Flip Functions -------------------------------------------------------------------  
local page = 1  
  
-- Turn Page -----------------------------------------------------------------------------  
function nextPage()  
 if ( page \< ttlPgs - 1) then -- If page is not last page  
 media.playSound( pgTurn ) -- Sound of page turning  
 transition.to( oddPgs[1], { xScale=0, x=halfW, time=turnSpeed } ) -- Furl the odd page  
 transition.to( evenPgs[1], { xScale=1, x=0+(page\*15), time=turnSpeed} ) -- Unfurl even page "(page\*15)" is for testing  
 transition.to( oddPgs[1], { alpha = 0, time = 0, delay=turnSpeed} ) -- Make odd page transparent (Corona bug scale = 0 doesn't actually make the width 0)  
   
 -- Load Next 2 Pages --------------------------------------------------------------  
 page = page + 2  
  
 -- Add next even page  
 image[page + 1] = display.newImage(pages[page + 1], fullW, 0) -- Display Even page  
 image[page + 1]:setReferencePoint(display.TopLeftReferencePoint) -- Use the top left reference for positioning  
 image[page + 1].xScale = 0.00001 -- Set the width to 0  
 evenPgs:insert( 1, image[page + 1] ) -- Insert it into the evenPgs array  
 evenPgs:insert( evenPgs[2] ) -- Move it to the begining of the array (index 1)  
  
 -- Add next odd page  
 image[page] = display.newImage(pages[page], halfW, 0) -- Display Odd page  
 image[page]:setReferencePoint(display.TopLeftReferencePoint) -- Use the top left reference  
 oddPgs:insert( 1, image[page] ) -- Insert it into the oddPgs array  
  
 -- Remove Previous 2 Pages --------------------------------------------------------  
 if(evenPgs.numChildren == 4) then  
 -- Remove even page that was viewed prior to the current page being displayed  
 evenPgs:remove(evenPgs[2]) -- Remove the even page below the one crrently being displayed from the evenPgs array   
 evenPgs[2] = nil -- Remove it from memory  
 end  
  
 -- Remove odd page that was viewed prior to the current page being displayed  
 oddPgs:remove(oddPgs[3]) -- Remove the odd page below the one crrently being displayed from the oddPgs array  
 oddPgs[3] = nil -- Remove it from memory  
  
 end  
end  
  
-- Page Turn Event -----------------------------------------------------------------------  
local function printTouch2( event )  
 if event.phase=="began" then  
 if event.x \> 600 then  
 if ( page \< ttlPgs - 1) then  
 nextPage()  
 else  
 -- End of book  
 end  
 end  
 if event.x \< 400 then  
 prevPage()  
 end  
 end  
end  
  
Runtime:addEventListener( "touch", printTouch2 )  
  

I’ve been struggling with this for several hours to no avail. My understanding is that when you put a new object into an array at index one, the indexs of all the rest of the objects in the array are incremented by one. Is this not true?

Thanks in advance! [import]uid: 6397 topic_id: 1517 reply_id: 301517[/import]

Your evenPgs and oddPgs are display groups and each time you insert an item, it’s added to the end. The first insert will be at evenPgs[1]. The next insert at evenPgs[2], etc. The display objects at the higher numbered indexes are displayed above the lower numbered indexes. If you insert an object that is already in the group it will be re-inserted (moved) to the end of the group and displayed on top of the other objects in the group. You can also specify the index where you want and object inserted.

When you remove an object from a group, all the other objects above it are shifted down.

I didn’t go through your code in detail but my guess is you are getting the error because your group doesn’t contain an object at that index postion. You could add some print statements to track the number of children in the group (evenPgs.numChildren) to understand what is going on. [import]uid: 7559 topic_id: 1517 reply_id: 4301[/import]

Thank you for this clear explanation of how the display objects are indexed. What I’m still confused about is in the lines:

evenPgs:remove(evenPgs[2]) -- Remove the even page below the one crrently being displayed from the evenPgs array   
evenPgs[2] = nil -- Remove it from memory  

evenPgs[2] is removed from the display correctly, but when I try to nil it I get the error.

Trying to nil evenPgs[1], [2] or [3] also gives me the error. If I change the line to “evenPgs[4] = nil” I no longer get the error but I still seem to be eating up the memory of the iPad because after the 12th page turn the application crashes. For the life of me I can’t seem to be able to wrap my head around this problem. I’ve been struggling with this for a long time and just seem to be going around in circles. [import]uid: 6397 topic_id: 1517 reply_id: 4396[/import]

Here’s the explanation the way I understand it:

evenPgs:remove(evenPgs[2]) does nil it and remove it from memory.

It was in the group, now it’s not. As long as that was the only reference to that display object (it’s existence in that display group), then it is garbage collected.

If your app is crashing due to running out of memory, you must have a leak somewhere else. [import]uid: 6678 topic_id: 1517 reply_id: 4401[/import]

Starting with Beta 7, removing an object from a group changes it from a display object to an ordinary table that Lua’s Garbage Collection will take care of if there are no other references to it (if it’s a Local variable). You don’t have to set it to nil.

There are two ways to remove an object from a group (they both do the same thing):
group:remove(object) – Like you are using.
object:removeSelf()

-Tom [import]uid: 7559 topic_id: 1517 reply_id: 4404[/import]

@DFox,

Removing an object doesn’t set it to nil – it removes the property that makes it a display object. If you had assigned other properties to the object before it was removed, they will still be there afterward. The only difference is you can’t insert it into another group (since it looks like an ordinary table).

local a = display.newCircle( 100, 100, 50 )  
a:setFillColor( 255, 255, 255 )  
a.name = "My Circle"  
  
a:removeSelf()  
  
print( a.name ) -- outputs "My Circle"  
display.getCurrentStage:insert( a ) -- generates an error because "a" is no longer a display object  

-Tom [import]uid: 7559 topic_id: 1517 reply_id: 4405[/import]

Hey Tom,

Thanks for the info, but I’m a little bit confused in this situation now:

[lua]local group = display.newGroup();
group:insert(display.newImage(“myimage.png”));
group[1].anyproperty = “whatever”;

group:remove(1);[/lua]
Now the display object was removed, but what about the table with the property? There’s no more references, so how would you nil it to be garbage collected? Is it taken care of since there’s no more references even though there’s a property on it?

Thanks! [import]uid: 6678 topic_id: 1517 reply_id: 4406[/import]

@DFox

You don’t need to nil anything to get it collected ever! Setting any reference to an object to nil is just that. Removing a reference. You could set it to anything else to.

But your question still is interesting. I think that group:remove(1) should remove both. The display object and the table object. Which results in that table not being referenced anymore. So it should get collected. [import]uid: 6928 topic_id: 1517 reply_id: 4430[/import]

Thanks, that’s what I thought, but I wasn’t sure…

One more question that I’ve never seen a clear answer on. How about event listeners? If you have event listeners on objects that get garbage collected or have no more references, will the listeners get collected to? Will the listeners prevent them from being collects? So yeah, I’m wondering how listeners work regarding garbage collecting. Right now I always remove them all, but it’s a pain. [import]uid: 6678 topic_id: 1517 reply_id: 4435[/import]

I am not sure what you refer to… maybe you wanna show an example code section. [import]uid: 6928 topic_id: 1517 reply_id: 4436[/import]

Example:

[lua]local group = display.newGroup();
group:insert(display.newImage(“whatever.png”);

group[1]:addEventListener(“touch”, touched);[/lua]

When you remove the group, and remove all references, do you need to remove the event listener from group[1]? Or is removing the group like group.parent:remove(group) and setting the reference to nil enough to have the listener collected? [import]uid: 6678 topic_id: 1517 reply_id: 4438[/import]

@DFox,

In your example, if you do a group:removeSelf(), you will remove all the display rendering elements of “group” and its objects (children). The non-display rendering portions, which includes the Event Listener, is not removed.

The removed display objects (group and children) are converted to simple Lua tables and subject to Lua’s GC (garbage collection). Same thing for the event listener. That means if there is no more references to those variables (tables), they will be GC’d by Lua. The display rendering portion of the objects consumes the majority of the object’s memory and is marked for immediate collection after the removeSelf(). [It should be noted that object:removeSelf() is same as group:remove(object)]

Setting an object to nil may not get rid of it if there is still a reference to the object.

Best practices say that when you remove an object or a group containing objects, you should not reference that group or the group’s display objects again. You should assume it will be GC’d when Lua does it normal collection.

You can safely insert (move) a display object or group from one group to another. You cannot remove a group or object and insert it into another group. That will generate an error because it’s no longer a display object.

I would also suggest removing the Event Listeners before removing the group (in your example).

-Tom [import]uid: 7559 topic_id: 1517 reply_id: 4517[/import]

I think the question was:

Does an event-lister needs a “removeEventLister()” to clean up correctly or does the deallocation of either the display object or the lua table includes all steps to clean up the event-listener.

Or with other words: Is “removeEventLister()” mandatory for cleanup or merely just a way to stop listening for the events.

That is implementation dependant and as I understand your answer @Tom the even-listener code does not generate stuff in the background which needs a “removeEventLister()” to be deallocated. You say you “would” remove the event listener. But is that mandatory? Because if it is… you have to remove it.

I GUESS that the implementation is something like a special value but normal variable in the Lua table object which gets traversed by the event dispatcher for all elements in the stage and all its sub group. Therefor it gets cleaned up together with the table object portion of the originating display object.

Hope I make sense :slight_smile: [import]uid: 6928 topic_id: 1517 reply_id: 4522[/import]

@OderWat,

If a listener is tied to a display object (e.g. for tap or touch events), and the display object goes away (removeSelf), I would expect the Lua GC to remove the listener since the reference has been removed too.

With that said, I would still remove the listener and not wait for Lua’s GC to do it for me.

Removing display objects with removeSelf, doesn’t rely on Lua’s GC and the texture memory is generally released within a few frames.

-Tom [import]uid: 7559 topic_id: 1517 reply_id: 4545[/import]

Thanks for the clarifications Tom.

I’ve always removed listeners, it’s just that it’s slightly annoying to do on groups :slight_smile: But really I’m pretty used to it because in ActionScript 3 if you don’t remove all event listeners from an object, the object never gets collected, so it becomes a habit to be careful and remove them all. [import]uid: 6678 topic_id: 1517 reply_id: 4546[/import]

Getting back to my original code, I’ve removed the nil statements and no longer get the error in the terminal but I still seem to have a memory leak. Everything worked fine before I put evenPgs in it’s own group so I’m really confused. I have no idea what could be causing this leak since I only have 4 images showing at any time. Could it have something to do with my listener?

Runtime:addEventListener( "touch", printTouch2 )

The way I understand it this listener applies to the stage and really has nothing to do with the images that I’m adding and removing. It simply calls the function that preforms these tasks. This seems like it should be a fairly simple thing to do, and I’m sure it is but for the life of me I haven’t got a clue as to what I’m doing wrong.

I’m excited about getting a handle on using Corona and appreciate everyones help with a total noob like me. If I can only get past this hurdle I can get started on the animations I want to add. I’m sure I’ll have LOTS of questions when I do that :))!
[import]uid: 6397 topic_id: 1517 reply_id: 4569[/import]

@tarenberg

From what I see without much analysis is that you add the images to the table “image” and to the group but only remove them from the group but not from the image table. [import]uid: 6928 topic_id: 1517 reply_id: 4571[/import]

Thanks OderWat That makes a lot of sense. It’s like a light just went on for me, groups and tables are 2 different things, duh :). Sometimes the obvious is so easy to miss! I’ll test this out as soon as I can. Thanks! [import]uid: 6397 topic_id: 1517 reply_id: 4576[/import]