Search Unity

Garbage Collection, Allocations, and Third Party Assets in the Asset Store

Discussion in 'General Discussion' started by Games-Foundry, Jun 21, 2012.

  1. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    Apologies, you're absolutely right. Truth be told my next post was going to be "if we carry this on it should really have its own thread". Mods, if someone would care to split this off that'd be awesome, thanks!
     
  2. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    @ronan.thibaudau: In an ideal world, people never make mistakes. That there would be no need for safety nets. You'd be surprised by the reality of it.
     
  3. ronan-thibaudau

    ronan-thibaudau

    Joined:
    Jun 29, 2012
    Posts:
    1,722
    What makes you think i live outside of the reality? As i said I've been working professionally in software development teams for many years, those things are a reality in my teams
     
  4. Dabeh

    Dabeh

    Joined:
    Oct 26, 2011
    Posts:
    1,614
    I agree.
    In some cases null-checks are okay IMO, but generally I avoid them. Typically I prefer to fix the root problem instead of writing hack code. If I have a null and I'm trying to use it for some bizarre reason and need a null check, I'm probably doing it wrong. It also makes stuff harder later down the track for numerous reasons.
     
    Last edited: Jun 18, 2013
  5. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    Would the anti-null checkers be kind enough to add one or more game projects to their signatures please? Rhetoric is all fine and well but without practical demonstration it lacks credibility IMHO.

    I also believe it's important to consider the nature of a product's commercial environment in such a debate. In our case that's Steam Early Access. A large community of players can be a vicious beast unless you are responsive - quick to publish new updates and quick to fix bugs. I fear that were we to adopt the approach proposed by Mr Thibaudau et al, whether or not each release is a masterpiece of engineering would be a moot point, because our community would have abandoned us for being too slow while vocally expressing dismay in the forums putting off potential customers. That is why practicality wins over idealism in our particular case. I'm not trying to push this approach onto anyone or convince them of a right or wrong way, merely presenting an approach for consideration that is working for us right now in a very real commercial situation.

    Of course the end objective is to arrive at a final stable and well engineered game. It's just a different path to achieving that goal.
     
    Last edited: Jun 18, 2013
  6. ronan-thibaudau

    ronan-thibaudau

    Joined:
    Jun 29, 2012
    Posts:
    1,722
    My customers are large banks grossing multi billion €, i'd say that's pretty credible no? You'd react much faster to your community too as i advocate typing less code and making it simpler, this means more time making changes feature, less time writing useless bloat. As i said i do this commercially full time and not as a game in progress kind of work but in shipped projects that are used by many for their day to day jobs.
     
  7. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    You might be advocating that but you aren't expressing it very well. The whole argument about fixing things in a GetComponentOrDefault method seems to be a poor example of the point being made. Its also unclear to me if you are saying you shouldn't do null checks, or that you shouldn't write API's that require consumers of the APIs to do null checks.

    I'm also curious why people consider null an error or something that has to be fixed in the context of GetComponent. One could argue that there should be a separation of concerns (with a HasComponent and a GetComponent), but given the API we have there are numerous use cases where you want to do different things depending on the presence or absence of a given component.

    EDIT: And no, I wouldn't think working for a big bank is particularly credible here. Having worked as a consultant for enterprise clients (including a couple of the big four banks over here) I wouldn't expect my development knowledge would transfer as well as someone whose experience comes from working in games development; particularly someone whose experience comes from indie games development with Unity.
     
    Last edited: Jun 18, 2013
  8. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    I would argue that business applications for banking clients are very different to games development, and the approach you have adopted is highly appropriate to the commercial environment in which you and your clients operate. However, what you are proposing doesn't necessarily translate well to other commercial environments, which without demonstrable experience in games development means you are relying on an assumption.

    Many of us in the forums including myself have spent years if not decades developing and managing distributed business applications for blue chip companies and central government agencies - it's a common route for games developers to pay the bills while honing the skills required to make a game. My post was not a challenge to the depth of your technical knowledge - it's evident from your other posts that you are technically very knowledgeable. What I am requesting, is a practical demonstration of where you have applied the technique you are championing in the field of games development. If you cannot provide such evidence to support your proposition, then it's going to be harder for you to convince developers as to the merits of your proposal.

    What myself and I believe others find objectionable is your assertion that yours is the best approach, when you have not provided us with convincing evidence or demonstrable practical experience in the field of games development.
     
    Last edited: Jun 18, 2013
  9. ronan-thibaudau

    ronan-thibaudau

    Joined:
    Jun 29, 2012
    Posts:
    1,722
    What i'm saying is neither but something in between, it's not null specific, it's for all undefined or unclear states, my take on it is "don't use an api that does that or, if forced to use one, wrap another API around it that handles those concerns", GetComponentOrDefault is an example, it changes the functionality (it expresses what it does clearly, return a default if none is found) but the fix is where it belongs, overall i just don't like the API, there's no use a GetComponents, it should be a Components.OfType<x> where you can just check for any before getting the first (or multiples) etc, but i don't want the API to fail and say "oh hey, take null and just guess what happened dude".

    A similar thing happens when you're doing UI work on applications, if you see lot of bad input or wrong user interaction handling, my first act is usually "dump all that code and stop letting the user do something that leads to errors, instead fix it at the root, the place where he does the error, instead of tossing in an error message after you've let him do it". It's pretty similar here, except the UI is the API and the end user is the dev, APIs should be simply, clean, expose clear functionality and do so in as much as possible an error free context, if you're API is full of cases where it can lead to unexpected state, it needs rewriten from scratch IMHO
     
  10. ronan-thibaudau

    ronan-thibaudau

    Joined:
    Jun 29, 2012
    Posts:
    1,722
    This has nothing to do "at all" with game development or not, C# is C#, code level diferences between diferent projects are trivial and mostly bogged down to API level, from driver programming to large business apps to websites to games it's really not that different if you're writing in the same language. I'm doing in unity work but not willing to release it for now however, but this is meaningless, none of the comments are gamedev specific in there, i've seen people doing exactly what you're doing in other apps, and happily rewriten it without.
     
  11. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    I agree with your general philosophy but again I think the example is a bad one. GetComponent doesn't say: "oh hey, take null and just guess what happened dude". It says "this GameObject doesn't have one of those components". Its quite well defined, and quite useful.

    I also think you need to be a little more open minded, different circumstances are generally suited to different approaches. Just because you had success with Brand X development approach doesn't mean that people can't be equally successful with Brand Y development approach.
     
    Last edited: Jun 18, 2013
  12. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    I strongly disagree with you, but have enjoyed the debate.
     
  13. ronan-thibaudau

    ronan-thibaudau

    Joined:
    Jun 29, 2012
    Posts:
    1,722
    Well ironically this is mostly about productivity and not about stability, once you see that you're not actually dropping quality / stability by doing that, you see you get to add a lot more feature per ($ / hour / whatever) spent as you get rid of the boilerplate code.
    Also sometimes a direct consequence is your whole project changes when taking on that mentality, sometimes all of the frameworks you build to manage complexity, once removed, make for a very small project whose complexity needn't be managed, i have a few applications examples where i did a (feature complete) rewrite from 40K LoC to 3K, and suddenly people realized how trivial the project was once we removed everything they did to compensate for it being complex.
    In the case of unity games and with the work unity does (regardless of whether or not i agree with the technical quality of the unity API / stability of unity) i feel most of the projects i've seen so far as unity project should really take little to no programming, although there's a lot of artwork going on.
    With the prototyping i'm doing i expect my first game to be no more than 20K LoC (asset store offerings excluded) if i stick with unity.
     
  14. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    Sorry I updated my previous post with some more details.
     
    Last edited: Jun 18, 2013
  15. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    I use GetComponent for configuration in several scripts, so a null check is actually intentional code.

    As for you Ronan, you are still writing software for a buggy OS, using buggy software to develop your bug free code. Not sure how you're achieving such zen...
     
  16. ronan-thibaudau

    ronan-thibaudau

    Joined:
    Jun 29, 2012
    Posts:
    1,722
    As i said my job is to write software for a certain OS/tool, not to unit test said OS/Tool, however this is largely false, so far in that many years of programming i fell on exactly "one" OS bug, i wouldn't call that a buggy OS at all now would you?

    Also i never said i develop bug free code, everyone does bugs, we're just mere humans and as humans we make errors, because we're tired or not focused, or for a miriad of other reasons, i just don't do any of the following because i consider them true "anti paterns", so it just doesn't happen.
     
  17. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    Granted OS bugs are less common, and definitely harder to prove as the cause, but Unity is by no means bug free.

    So what I'm getting from you, is writing defensively is stupid, despite the fact everyone writes buggy code?

    k
     
  18. ronan-thibaudau

    ronan-thibaudau

    Joined:
    Jun 29, 2012
    Posts:
    1,722
    No what you get from me, is that you're not writing defensively at all, you're just writing a bunch of code, somehow hoping it "defends" you against things that you can make provable as not happening, but instead chose to defend against "just in case" while increasing your code base and introducing more bugs. Basically my take on this is your way to write this = more bugs than my way to write this + lesser productivity + lower maintainability.

    And yea unity isn't bug free, spot the bugs report them or switch tools, but definately don't program around the bugs, bugs are meant to be fixed where they are, if they're in unity, make a bug report wait (but i'll grant you that if it was that easy, we wouldn't need this thread, it's not really a general issue however, it's unity specific, a lot of other API providers i worked with fix any form of important bug immediately, hell i've had cases where going from a bug report to a fixed custom dll took less than 8 hours, and a product update less than 3 days with other providers).
     
  19. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    Since you've mentioned zen, everything is stupid.
     
  20. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    I can agree that if your defensive code just 'hides' problems, then yes, you're doing it wrong.

    However, its debatable whether a null check (which can qualify as defensive or logic), is worse than 'doing it properly'. TBH, im not actually sure what kind of defensive coding you're talking about as bad. Maybe you could provide an example of what you consider bad defensive coding...


    eg.. I have a tank, tank gets assigned a target to shoot at. target gets destroyed (by some other tank). tank target is now null.

    I have two options here.

    1. Check if target is null, if it is, assign new target. One LOC and I have ensured that target is never null, and if it is, I know, and handle that. However, this qualifies as both logic and defensive coding.

    2. Create a system that notifies tank that its target has been destroyed. I can promise you, this is a lot more that one LOC, and a lot harder to maintain than the simple null check.


    Make a bug report and wait!? Might just hold off development until the new GUI comes out as well.


    Anyhoo, I find this entire discussion pointless. Everyone has there own preferences for how things should be done... Its kind of like arguing about coding standards. Therefore, this will be my last comment related to defensive coding. Id rather this thread stayed true to its original purpose. Squeezing frames out of code.
     
  21. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    This is the issu I have with ronan's suggested approach. GetExistingComponentOrDefault() could hide a myriad of errors. Sure, I can make it "provable" that it will never return null, but that alone doesn't mean the system it is a part of is operating as intended.

    Here's a scenario:
    - Person A writes some code that uses GetExistingComponentOrDefault().
    - Later on, Person B goes and sets up a non-default component on a specific object. (After all, if non-default stuff wasn't desired you wouldn't have an "OrDefault" in the first place.)
    - Later on, Person C writes some code that does something with a collider and then, having finished with the collider, deletes it intentionally. Unfortunately, due to inexperience / 40 hours with no sleep / whatever they delete the wrong collider in the collision pair.

    This creates two problems: the collider that is meant to be deleted is not, and the specially configured one that is meant to exist gets replaced with a generic default.

    Using my approach, this will throw up error logs (most likely during development), and will probably will get found and have both problems fixed. Even if the situation is so obscure that it's missed in development, logs in the wild will tell me what's going on post release for fast maintenance. (Some people would even argue that it's better to log it and still let it crash the app, because that forces whoever finds it to fix it there and then. I'm not sure I'd go that far, though.)

    Using ronan's approach as I understand it, the collider will just get replaced with a default. There will probably be no log, because he doesn't care about that. There will be misbehaviour in the app under possibly quite obscure scenarios. The combination of these factors result in the problem being much more unlikely to be found during development. Sure the problem won't manifest often, and GetExistingComponentOrDefault() is still "provably correct" because it does indeed always return a valid component. But it's still a problem. I don't consider that "fixed by design", I consider that to be a problem that's getting hidden.

    Just to be clear, here, by "fixed" I don't mean "the calling code got what it required to operate without crashing the app", I mean "the application operated as intended by the designer under as many usage scenarios as possible, preferably all of them". In my mind, returning a freshly generated default component is very nearly as bad (maybe even actually as bad) as returning null. If I expected a particular collider and I got something else then something is broken regardless of whether the wrong something is a "null" or a "default" that manages to hide the error. And personally I'd rather that the error was exposed so I could fix it than hidden where it could fester.

    As for "definately don't program around the bugs... make a bug report and wait"... well...
     
    Last edited: Jun 19, 2013
  22. ronan-thibaudau

    ronan-thibaudau

    Joined:
    Jun 29, 2012
    Posts:
    1,722
    As i said you can very well log "inside" the GetComponentOrDefault (or similar method), so you'd still see that poping up and as i've said many times my post wasn't specifically about null but just about writing good code overall instead of letting bad code pollute your own with checks everywhere. However on the null topic i suggest you watch the talk "my billion dollar mitake" by the dude who invented null, and basically says he's so sorry for doing that.
     
  23. Smooth-P

    Smooth-P

    Joined:
    Sep 15, 2012
    Posts:
    214
    All of my APIs, and my wrappers for Unity / 3rd party APIs that I have to use (including GetComponent<T>()), return an Option<T> when there may or may not be an object to return.

    This results in extremely clean code with solid, meaningful semantics. Of course, it was a helluvalot cleaner before I realized how insanely bad the GC is and I didn't worry about foreach / Linq allocating tons of 0 / 1 element iterators or object boxes that last for a split-split millisecond...
     
    Last edited: Jun 20, 2013
  24. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
  25. Mr.T

    Mr.T

    Joined:
    Jan 1, 2011
    Posts:
    546
    Thanks for that.

    In your experience while making your game, which ones among those 9 tips that Andrew offers, had the most impact?
     
    Last edited: Aug 10, 2013
  26. AndrewGrayGames

    AndrewGrayGames

    Joined:
    Nov 19, 2009
    Posts:
    3,821
    The way I was taught defensive programming in C#, was to act as a argument-sanity check, which is to say, throw exceptions up-front when a method is passed garbage. This way, you're not hiding any problems; the code is able to declare that garbage is being passed, which helps with the debugging process.
     
  27. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    The techniques covered in this thread and Andrew's post mostly help with reducing the frequency of the well documented garbage collector stutter issue by avoiding unnecessary allocation. The biggest wins on Folk Tale are the usual suspects of object pooling, caching variables, and regularly checking the profiler to see when allocations creep in so I can refactor code. I've got to review where we can take advantage of structs, and longer term we'll be reducing the overall variable count so that when gc does kick in it's got fewer references to check, but that's low priority as we have allocation under control now; just the Unity API which is the offender hence the emphasis now on requesting an API overhaul instead of a Mono upgrade ( which sounds like it isn't going to happen if are in the beta program group ).

    The greatest impact on memory consumption continues to be smarter design and iteration to address the biggest memory hogs: shifting audio from in-memory to be streamed on demand ( large amounts of voice acting ), smarter structuring of textures and the way we pack channels and use them with shaders, and using a universal skeleton for each character race so that we have one set of animation data ( using the legacy animation system because mechanim doesn't have inbuilt events system unless you buy the one on the AS).
     
    Last edited: Aug 11, 2013
  28. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    This explanation is wrong. Most of the standard enumerators are actually structs and are not usually allocated on the heap. Enumerators for arrays are classes but in the case of arrays foreach-loop doesn't use enumerators and behaves like a for-loop, no need to worry at all.

    This code allocates nothing:
    Code (csharp):
    1. var list = new List<GameObject>();
    2. // populate the list
    3.  
    4. var enumerator = list.GetEnumerator();
    5. while (enumerator.MoveNext())
    6. {
    7.     var item = enumerator.Current;
    8.     // do something
    9. }
    10.  
    What actually makes foreach-loop to allocate is boxing. Foreach-loop actually does this
    Code (csharp):
    1. var list = new List<GameObject>();
    2. // populate the list
    3.  
    4. using (var enumerator = list.GetEnumerator())
    5. {
    6.     while (enumerator.MoveNext())
    7.     {
    8.         var item = enumerator.Current;
    9.         // do something
    10.     }
    11. }
    12.  
    which means:
    Code (csharp):
    1. var list = new List<GameObject>();
    2. // populate the list
    3.  
    4. var enumerator = list.GetEnumerator();
    5. try
    6. {
    7.     while (enumerator.MoveNext())
    8.     {
    9.         var item = enumerator.Current;
    10.         // do something
    11.     }
    12. }
    13. finally
    14. {
    15.     ((IDisposable)enumerator).Dispose(); // <-- here happens the boxing
    16. }
    17.  
     
  29. Mr.T

    Mr.T

    Joined:
    Jan 1, 2011
    Posts:
    546
    That is some interesting insight, especially the part about an universal skeleton. Thanks again
     
  30. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    I didn't make it through every post here, but one thing I also wanted to bring up that I didn't see discussed was Heap Complexity. The size of the heap doesn't make as much difference with generational garbage collection, but the complexity of the heap does.

    Consider this...

    Code (csharp):
    1.  
    2.   var intArray = new int[] { 1, 2, 3, 4 ,5 };
    3.  
    The array is a reference type, but it is populated with value types. The value types actually exist on the stack in this instance and collecting the array is pretty simple. But now let's say we have 4 "Person"s in an array:

    Code (csharp):
    1.  
    2.    var pArray = new Person[] { person1, person2, person3, person4 };
    3.  
    Now assume that person1 through person4 have been instantiated elsewhere. This array is now an array of value type objects (assuming Person is a class). In order to collect pArray, the garbage collector must also examine every element within pArray (each Person object) to make sure they are eligible for collection and have no more references.

    Now, you could actually have a small amount of actual memory allocation by instantiating tens of thousands of tiny class objects, however if they all reference eachother, you've created quite the web of references for the GC to have to follow. It would also likely take more than one iteration for the GC to actually collect everything as person1, person2, person3, and person4 may be all collected in one cycle while pArray is collected in a subsequent generation.
     
  31. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    There's a way to allocate an array fully on stack:
    Code (csharp):
    1. unsafe
    2. {
    3.     int* p = stackalloc int[10];
    4.     for (int i = 0; i < 10; i++)
    5.     {
    6.         p[i] = i;
    7.     }
    8.  
    9.     for (int i = 0; i < 10; i++)
    10.     {
    11.         UnityEngine.Debug.Log("address: " + (ulong)(p + i) + ", value: " + p[i]);
    12.     }
    13. }
    There's also a way to allocate memory and keep the GC happy at the same time:
    Code (csharp):
    1. unsafe
    2. {
    3.     var pointer = (byte*)Marshal.AllocHGlobal(1024); // allocates 1024 bytes on unmanaged heap
    4.     for (int i = 0; i < 1024; i++)
    5.     {
    6.         pointer[i] = 0;
    7.     }
    8.     Marshal.FreeHGlobal((IntPtr)pointer);
    9. }
     
  32. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,659
    Unsafe blocks aren't permitted in Unity, so I don't see the point in saying that it can be done if you use them.
     
  33. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    Not permitted by default in user scripts, but there's a pretty simple trick to unlock them:

    1. If you use Net 2.0, make a text file called gmcs.rsp with a single line inside:
    Code (csharp):
    1. -unsafe
    and save it to your Assets folder.

    2. If you use Net 2.0 Subset, do the same, but the name should be smcs.rsp

    --
    Another hint: the code compiled into dlls outside of Unity may be unsafe. Unity doesn't check or doesn't care.
     
  34. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,659
    Huh. Which platforms have you tried that with?
     
  35. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    It works on Windows.
    It may work on Mac and Linux too (I can build a project and get no errors, but can't test it).
    It doesn't work if I choose a Web Player, but it is expected.
     
  36. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,659
    Neat. I wonder if it works smoothly on the AOT platforms too.
     
  37. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    It also won't work on Windows Phone, probably also not Windows 8 since RT apps are sandboxed, and likely not on any other mobile platform either.
     
  38. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    Anyone try the unsafe code on iOS yet?
     
  39. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    unsafe works fine on iOS - UChess (available in the asset store *grins*) uses a bunch of unsafe code for fast move hashes and works fine on iOS.
     
    Last edited: Aug 16, 2013
  40. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    Could you explain a bit more about what that means, and how much benefit using unsafe code actually provided?
     
  41. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    As far as I know, in .NET/Mono "unsafe code" is marked blocks of code where you do your own memory management rather than having the runtime do it for you. This takes load off the GC because it doesn't have to pay attention to what you're doing there, but increases risk/development time because you have to pay attention and look after what's going on (a-la non-managed programming... so it's not the end of the world, it's just far less hand-holdy than normal C#).

    It may also loosen up on things like automated bounds checking, but I'm not sure.
     
  42. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    I was just confirming that you can use the unsafe flag/unsafe code on iOS.
     
  43. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,659
    'Unsafe' allows you to work directly with pointers.

    No, it doesn't really have anything to do with the GC. What you might be thinking of is the 'fixed' statement which tells the GC not to move or collect an object for the duration of the fixed block.

    If you work with arrays via pointers then yes there's no automated bounds checking.

    More information
     
  44. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Getting very high GC Alloc on NGUI's UIPanel.FIll(), one call is generating around 50kb a frame, admittedly this is when the AI is running the game at high speed but it's still a lot.

    Digging down...

     
  45. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
    In my opinion. Unity should rewrite their API so that, for example GetComponents<T> should have the signature.
    Code (csharp):
    1. public T[] GetComponents<T> ( T[] buffer = null );
    The buffer would be filled with the data, except if it was null or if it was too small, then a new buffer would be allocated and returned.
    The great thing about this change is that it is backwards compatible. No existing code needs changing since the optional parameter will just be set to null and therefore a new buffer allocated (just as before). And one cannot argue that the previous version is easier to learn, because you can still use that version if you want. It could be mentioned in the "Advanced" section that you can pass a buffer to it so that new users don't have to be confronted with this "huge" complexity.

    [EDIT] The buffer would be "null terminated", e.g if a buffer with length 4 was filled with 2 components, the 3rd index would be set to null to indicate that there are no more components.

    For some calls it would require some changes. E.g mesh.vertices would have to be changed to

    Code (csharp):
    1. GetVertices (Vector3[] buffer = null);
    This accidentally matches the GetTriangles methods which is already in the mesh class.
    The mesh.vertices property can still be kept of course.
     
  46. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    What is the advantage of your proposed method?

    I can't find a GetTriangles() method like this. Can you point me to it?
     
  47. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
  48. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    The problem with this approach is that your buffer then holds on to references to those components which would not allow them to go out of scope when the object is destroyed.

    Also, you should really avoid using default values for parameters when possible as it has a little known gotcha. The default values are actually stored in the metadata of the assembly that references it. Here's an example..

    Let's say Unity 4.2 has a method on their API that has a default:

    Code (csharp):
    1.  
    2. public bool DoSomething(int someValue = 5)
    3. {
    4.  
    5. }
    6.  
    Now, you create a code asset that is compiled to a DLL and you reference UnityEngine.dll and build your assembly. All is fine. Now, let's say Unity decides that want to change the default value in their API and change the signature to:
    Code (csharp):
    1.  
    2. public bool DoSomething(int someValue =  0)
    3. {
    4. }
    5.  
    That seems harmless enough... however, any time you call that method from your own assembly it is still going to get the value of 5. Why is this? It's because the defaults are actually part of the assembly metadata. In order to leverage the new default value, your assembly has to be recompiled against the newer API. This would also break backward compatibility.
     
  49. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    Your buffer also might be too small, and you can only be sure to get the right number when you have at least one null element. Doesn't sound very efficient to me. [edit]I see that you thought of this case, but somehow this all makes it awkward to use.[/edit
     
    Last edited: Oct 30, 2013
  50. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
    But with unity you would have to recompile it anyway for each build. I don't see the problem with that.

    If you really want to avoid that, just loop through it and set it to null after you have used it.
    If you think it is too much code, just write an extension method like .Clear to do it for you.
    And if you don't like it at all. The previous approach works in exactly the same way even with this update to the signature.