Foreach is bad, so how to get the object child?

According to discussion in here:

http://forum.unity3d.com/viewtopic.php?t=30745&postdays=0&postorder=asc&start=30

using foreach / for…in is not really recommended due to memory allocation thingie.

So how am i suppose to get the children of an object without using foreach/for…in ?

Kenlem actually asking the same question on the same thread, but i guess it’s somehow left unanswered,
so i’m just gonna repost his code in here (thx Kenlem):

http://forum.unity3d.com/viewtopic.php?p=195116#195116

Transform xform;
int index;
for (index=0; index<root.childCount); index++)   
{
   xform = root.???;
   ...
}

anyone has alternative solution ?

thanks in advance.

Honestly, I’ve been at this for 6 months plus now… And I still couldn’t give you a good answer on that.

In fact, I’ve come up with work arounds (broadcast message, SetActiveRecursivly, etc), caching or designing around it (setting it all on Awake/Start, etc).

I look forward to someone giving you a hand on this one :slight_smile:

(Dremora, that’s your queue to show us your awesomeness… Or for Eric to say something entertaining)

It’s called boxing and unboxing. It is the conversion of a reference type to a value type. For example, if you have an ArrayList and you add Int32 items to that list they are first converted to “object”.

In the case of Transform, that inherits from IEnumerable. Consider this:

IEnumerator iter = transform.GetEnumerator();
iter.Reset();
while (iter.MoveNext())
{
    Transform t = (Transform)iter.Current;
    Debug.Log(t.name);
}

The enumerator (iterator) travels over all the elements in the transform collection. But it doesn’t know what type it is when we do “iter.Current”. Therefore we cast because we know what we’re trying to do. And subsequently make a temporary object. Now consider:

foreach (Transform iter in transform)
{
    Debug.Log(iter.name);
}

In this version the cast is implied by the foreach. But in the actual code that the compiler generates, it’s the same as the enumerator version.

This is why generics is so handy. You don’t have to box and unbox.

Hope I got that right

You got that the wrong way around. (Hope that was entertaining enough for you.)

–Eric

Transform xform;
int index;
Transform[] ts = root.GetComponentsInChildren<Transform>();
int len = ts.Count;

for (index=0; index<len; index++)   
{
   xform = ts[i];
}

If there are no generics in Unity iPhone yet, get the components in some other way :slight_smile:

Why not create an array of children in Awake(), and only change the array if the number of children changes?

zibba - Does unboxing have the same memory problem as using foreach?

As far as I know, boxing\unboxing is not very good in terms of performance :roll:

Yep no generics in iPhone yet which is unfortunate. The “iPhone way” would be:

Component[] ts = gameObject.GetComponentsInChildren(typeof(Transform));
foreach (Transform t in ts)
{
}

Unboxing is an explicit conversion from the type “object” to a value type. First it has to check the object instance to make sure it is a boxed value, then it copies the value from the instance into the value type variable. The classic example being:

int i = 123; // value type
object o = i; // boxing
int j = (int)o; // unboxing

In this case it’s more of a casting overhead. The memory overhead has already happened when it was first boxed.

edit: fixed typos.

Yes caching is good though it uses extra memory just not every frame.

thanks a lot for the feedback guys, really appreciate it, keep em coming :smile:

btw problem with caching is, i have lots of object with this children-parent thingie, so it’s kinda pain to cache everything.
Also some of the object is Instantiated on the fly, so it’s not possible to cache those. (maybe ??)

would be nice to have access to something simple like:

transform.child[0]
transform.child[1]
transform.child[2]

where child : Transform
:?

So, I was really curious about all this so I found a way to install .NET 1.1 in Visual Studio 2008 (you would think it would be easy) and did some test with LDASM. From my very informal tests it appears you can actually avoid a cast and just get an isinst call by using the ‘as’ operator. This is significantly faster when iterating large collections but honestly for this case I cant see there ever being thousands - millions of Transforms so it really, really doesnt matter. That being said, here is (untested) code that should compile down to just an isinst call rather than casting.

IEnumerator iter = transform.GetEnumerator();
iter.Reset();
while (iter.MoveNext())
{
    Transform t = iter.Current as Transform;
    Debug.Log(t.name); 
}

How about this:

game_object.transform.GetChild(0);
game_object.transform.GetChild(1);
game_object.transform.GetChild(2);

Undocumented, but works fine :slight_smile: